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}