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