trust_dns_proto/rr/rdata/
txt.rs

1/*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! text records for storing arbitrary data
18use std::fmt;
19use std::slice::Iter;
20
21#[cfg(feature = "serde-config")]
22use serde::{Deserialize, Serialize};
23
24use crate::error::*;
25use crate::serialize::binary::*;
26
27/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
28///
29/// ```text
30/// 3.3.14. TXT RDATA format
31///
32///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
33///     /                   TXT-DATA                    /
34///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
35///
36///
37/// TXT RRs are used to hold descriptive text.  The semantics of the text
38/// depends on the domain where it is found.
39/// ```
40#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
41#[derive(Debug, PartialEq, Eq, Hash, Clone)]
42pub struct TXT {
43    txt_data: Box<[Box<[u8]>]>,
44}
45
46impl TXT {
47    /// Creates a new TXT record data.
48    ///
49    /// # Arguments
50    ///
51    /// * `txt_data` - the set of strings which make up the txt_data.
52    ///
53    /// # Return value
54    ///
55    /// The new TXT record data.
56    pub fn new(txt_data: Vec<String>) -> Self {
57        Self {
58            txt_data: txt_data
59                .into_iter()
60                .map(|s| s.into_bytes().into_boxed_slice())
61                .collect::<Vec<_>>()
62                .into_boxed_slice(),
63        }
64    }
65
66    /// Creates a new TXT record data from bytes.
67    /// Allows creating binary record data.
68    ///
69    /// # Arguments
70    ///
71    /// * `txt_data` - the set of bytes which make up the txt_data.
72    ///
73    /// # Return value
74    ///
75    /// The new TXT record data.
76    pub fn from_bytes(txt_data: Vec<&[u8]>) -> Self {
77        Self {
78            txt_data: txt_data
79                .into_iter()
80                .map(|s| s.to_vec().into_boxed_slice())
81                .collect::<Vec<_>>()
82                .into_boxed_slice(),
83        }
84    }
85
86    /// ```text
87    /// TXT-DATA        One or more <character-string>s.
88    /// ```
89    pub fn txt_data(&self) -> &[Box<[u8]>] {
90        &self.txt_data
91    }
92
93    /// Returns an iterator over the arrays in the txt data
94    pub fn iter(&self) -> Iter<'_, Box<[u8]>> {
95        self.txt_data.iter()
96    }
97}
98
99/// Read the RData from the given Decoder
100pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<TXT> {
101    let data_len = decoder.len();
102    let mut strings = Vec::with_capacity(1);
103
104    // no unsafe usage of rdata length after this point
105    let rdata_length =
106        rdata_length.map(|u| u as usize).unverified(/*used as a higher bound, safely*/);
107    while data_len - decoder.len() < rdata_length {
108        let string =
109            decoder.read_character_data()?.unverified(/*any data should be validate in TXT usage*/);
110        strings.push(string.to_vec().into_boxed_slice());
111    }
112    Ok(TXT {
113        txt_data: strings.into_boxed_slice(),
114    })
115}
116
117/// Write the RData from the given Decoder
118pub fn emit(encoder: &mut BinEncoder<'_>, txt: &TXT) -> ProtoResult<()> {
119    for s in txt.txt_data() {
120        encoder.emit_character_data(s)?;
121    }
122
123    Ok(())
124}
125
126impl fmt::Display for TXT {
127    /// Format a [TXT] with lossy conversion of invalid utf8.
128    ///
129    /// ## Case of invalid utf8
130    ///
131    /// Invalid utf8 will be converted to:
132    /// `U+FFFD REPLACEMENT CHARACTER`, which looks like this: �
133    ///
134    /// Same behaviour as `alloc::string::String::from_utf8_lossy`.
135    /// ```rust
136    /// # use trust_dns_proto::rr::rdata::TXT;
137    /// let first_bytes = b"Invalid utf8 <\xF0\x90\x80>.";
138    /// let second_bytes = b" Valid utf8 <\xF0\x9F\xA4\xA3>";
139    /// let rdata: Vec<&[u8]> = vec![first_bytes, second_bytes];
140    /// let txt = TXT::from_bytes(rdata);
141    ///
142    /// let tested = format!("{}", txt);
143    /// assert_eq!(
144    ///     tested.as_bytes(),
145    ///     b"Invalid utf8 <\xEF\xBF\xBD>. Valid utf8 <\xF0\x9F\xA4\xA3>",
146    ///     "Utf8 lossy conversion error! Mismatch between input and expected"
147    /// );
148    /// ```
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
150        for txt in self.txt_data.iter() {
151            f.write_str(&String::from_utf8_lossy(txt))?;
152        }
153
154        Ok(())
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    #![allow(clippy::dbg_macro, clippy::print_stdout)]
161
162    use super::*;
163
164    #[test]
165    fn test() {
166        let rdata = TXT::new(vec!["Test me some".to_string(), "more please".to_string()]);
167
168        let mut bytes = Vec::new();
169        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
170        assert!(emit(&mut encoder, &rdata).is_ok());
171        let bytes = encoder.into_bytes();
172
173        println!("bytes: {:?}", bytes);
174
175        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
176        let restrict = Restrict::new(bytes.len() as u16);
177        let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
178        assert_eq!(rdata, read_rdata);
179    }
180
181    #[test]
182    fn publish_binary_txt_record() {
183        let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
184        let rdata = TXT::from_bytes(vec![b"Test me some", &bin_data]);
185
186        let mut bytes = Vec::new();
187        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
188        assert!(emit(&mut encoder, &rdata).is_ok());
189        let bytes = encoder.into_bytes();
190
191        println!("bytes: {:?}", bytes);
192
193        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
194        let restrict = Restrict::new(bytes.len() as u16);
195        let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
196        assert_eq!(rdata, read_rdata);
197    }
198}