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}