hickory_proto/rr/rdata/
hinfo.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! HINFO record for storing host information
9
10use alloc::{boxed::Box, string::String};
11use core::fmt;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::{
17    error::*,
18    rr::{RData, RecordData, RecordType},
19    serialize::binary::*,
20};
21
22/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987][rfc1035]
23///
24/// ```text
25/// 3.3.2. HINFO RDATA format
26///
27///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
28///     /                      CPU                      /
29///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
30///     /                       OS                      /
31///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
32///
33/// where:
34///
35/// CPU             A <character-string> which specifies the CPU type.
36///
37/// OS              A <character-string> which specifies the operating
38///                 system type.
39///
40/// Standard values for CPU and OS can be found in [RFC-1010].
41///
42/// HINFO records are used to acquire general information about a host.  The
43/// main use is for protocols such as FTP that can use special procedures
44/// when talking between machines or operating systems of the same type.
45/// ```
46///
47/// [rfc1035]: https://tools.ietf.org/html/rfc1035
48#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
49#[derive(Debug, PartialEq, Eq, Hash, Clone)]
50pub struct HINFO {
51    cpu: Box<[u8]>,
52    os: Box<[u8]>,
53}
54
55impl HINFO {
56    /// Creates a new HINFO record data.
57    ///
58    /// # Arguments
59    ///
60    /// * `cpu` - A `character-string` which specifies the CPU type.
61    /// * `os` - A `character-string` which specifies the operating system type.
62    ///
63    /// # Return value
64    ///
65    /// The new HINFO record data.
66    pub fn new(cpu: String, os: String) -> Self {
67        Self {
68            cpu: cpu.into_bytes().into_boxed_slice(),
69            os: os.into_bytes().into_boxed_slice(),
70        }
71    }
72
73    /// Creates a new HINFO record data from bytes.
74    /// Allows creating binary record data.
75    ///
76    /// # Arguments
77    ///
78    /// * `cpu` - A `character-string` which specifies the CPU type.
79    /// * `os` - A `character-string` which specifies the operating system type.
80    ///
81    /// # Return value
82    ///
83    /// The new HINFO record data.
84    pub fn from_bytes(cpu: Box<[u8]>, os: Box<[u8]>) -> Self {
85        Self { cpu, os }
86    }
87
88    /// A `character-string` which specifies the CPU type.
89    pub fn cpu(&self) -> &[u8] {
90        &self.cpu
91    }
92
93    /// A `character-string` which specifies the operating system type.
94    pub fn os(&self) -> &[u8] {
95        &self.os
96    }
97}
98
99impl BinEncodable for HINFO {
100    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
101        encoder.emit_character_data(&self.cpu)?;
102        encoder.emit_character_data(&self.os)?;
103
104        Ok(())
105    }
106}
107
108impl<'r> BinDecodable<'r> for HINFO {
109    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
110        let cpu = decoder.read_character_data()?
111        .unverified(/*any data should be validate in HINFO CPU usage*/)
112        .to_vec()
113        .into_boxed_slice();
114        let os = decoder.read_character_data()?
115        .unverified(/*any data should be validate in HINFO OS usage*/)
116        .to_vec()
117        .into_boxed_slice();
118
119        Ok(Self { cpu, os })
120    }
121}
122
123impl RecordData for HINFO {
124    fn try_from_rdata(data: RData) -> Result<Self, RData> {
125        match data {
126            RData::HINFO(csync) => Ok(csync),
127            _ => Err(data),
128        }
129    }
130
131    fn try_borrow(data: &RData) -> Option<&Self> {
132        match data {
133            RData::HINFO(csync) => Some(csync),
134            _ => None,
135        }
136    }
137
138    fn record_type(&self) -> RecordType {
139        RecordType::HINFO
140    }
141
142    fn into_rdata(self) -> RData {
143        RData::HINFO(self)
144    }
145}
146
147/// [RFC 1033](https://tools.ietf.org/html/rfc1033), DOMAIN OPERATIONS GUIDE, November 1987
148///
149/// ```text
150/// HINFO (Host Info)
151///
152///            <host>   [<ttl>] [<class>]   HINFO   <hardware>   <software>
153///
154///    The HINFO record gives information about a particular host.  The data
155///    is two strings separated by whitespace.  The first string is a
156///    hardware description and the second is software.  The hardware is
157///    usually a manufacturer name followed by a dash and model designation.
158///    The software string is usually the name of the operating system.
159///
160///    Official HINFO types can be found in the latest Assigned Numbers RFC,
161///    the latest of which is RFC-1010.  The Hardware type is called the
162///    Machine name and the Software type is called the System name.
163///
164///    Some sample HINFO records:
165///
166///            SRI-NIC.ARPA.           HINFO   DEC-2060 TOPS20
167///            UCBARPA.Berkeley.EDU.   HINFO   VAX-11/780 UNIX
168/// ```
169impl fmt::Display for HINFO {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
171        write!(
172            f,
173            "{cpu} {os}",
174            cpu = &String::from_utf8_lossy(&self.cpu),
175            os = &String::from_utf8_lossy(&self.os)
176        )?;
177        Ok(())
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    #![allow(clippy::dbg_macro, clippy::print_stdout)]
184
185    use alloc::{string::ToString, vec::Vec};
186    #[cfg(feature = "std")]
187    use std::println;
188
189    use super::*;
190
191    #[test]
192    fn test() {
193        let rdata = HINFO::new("cpu".to_string(), "os".to_string());
194
195        let mut bytes = Vec::new();
196        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
197        assert!(rdata.emit(&mut encoder).is_ok());
198        let bytes = encoder.into_bytes();
199
200        #[cfg(feature = "std")]
201        println!("bytes: {bytes:?}");
202
203        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
204        let read_rdata = HINFO::read(&mut decoder).expect("Decoding error");
205        assert_eq!(rdata, read_rdata);
206    }
207
208    #[test]
209    fn test_binary() {
210        let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
211        let rdata = HINFO::from_bytes(
212            b"cpu".to_vec().into_boxed_slice(),
213            bin_data.into_boxed_slice(),
214        );
215
216        let mut bytes = Vec::new();
217        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
218        assert!(rdata.emit(&mut encoder).is_ok());
219        let bytes = encoder.into_bytes();
220
221        #[cfg(feature = "std")]
222        println!("bytes: {bytes:?}");
223
224        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
225        let read_rdata = HINFO::read(&mut decoder).expect("Decoding error");
226        assert_eq!(rdata, read_rdata);
227    }
228}