1use crate::{Buffer, DnsError, DnsMessage, DnsMessageError, MutBuffer};
2use crate::rdata::{RData, RDataParse};
3use crate::write::WriteBytes;
4
5#[derive(Copy, Clone, Debug, PartialEq)]
8pub struct Txt<'a> {
9 data: &'a [u8],
11}
12
13#[macro_export]
30macro_rules! dns_txt {
31 ($value:expr $(, $values:expr)* $(,)?) => {
32 {
33 const TXT: [u8; { $value.len() + 1 $(+ $values.len() + 1)* }] = {
34 if $value.len() > u8::MAX as usize {
35 panic!("Txt value too long, maximum length is 255.");
36 }
37
38 let mut result = [0; $value.len() + 1 $(+ $values.len() + 1)*];
39 result[0] = $value.len() as u8;
40
41 let mut index = 1;
42 let mut r_index = 0;
43 loop {
44 if r_index == $value.len() {
45 break;
46 }
47
48 result[index] = $value[r_index];
49 r_index += 1;
50 index += 1;
51 }
52
53 $(
54 if $values.len() > u8::MAX as usize {
55 panic!("Txt value too long, maximum length is 255.");
56 }
57
58 result[index] = $values.len() as u8;
59 index += 1;
60
61 let mut r_index = 0;
62 loop {
63 if r_index == $values.len() {
64 break;
65 }
66
67 result[index] = $values[r_index];
68 r_index += 1;
69 index += 1;
70 }
71 )*
72
73 result
74 };
75
76 unsafe { flex_dns::rdata::Txt::new_unchecked(&TXT) }
77 }
78 };
79}
80
81impl<'a> Txt<'a> {
82 #[inline(always)]
95 pub fn new(data: &'a [u8]) -> Result<Self, DnsMessageError> {
96 for r in (TxtIterator {
97 data,
98 pos: 0,
99 }) {
100 r?;
101 }
102
103 Ok(Self {
104 data,
105 })
106 }
107
108 #[inline(always)]
112 pub const unsafe fn new_unchecked(data: &'a [u8]) -> Self {
113 Self {
114 data,
115 }
116 }
117
118 #[inline(always)]
120 pub fn iter(&self) -> TxtIterator<'a> {
121 TxtIterator {
122 data: self.data,
123 pos: 0,
124 }
125 }
126}
127
128impl<'a> RDataParse<'a> for Txt<'a> {
129 #[inline]
130 fn parse(rdata: &RData<'a>, i: &mut usize) -> Result<Self, DnsMessageError> {
131 let data = &rdata.buffer[*i..*i + rdata.len];
132 *i += rdata.len;
133
134 Ok(Self {
135 data,
136 })
137 }
138}
139
140impl<'a> WriteBytes for Txt<'a> {
141 #[inline]
142 fn write<
143 const PTR_STORAGE: usize,
144 const DNS_SECTION: usize,
145 B: MutBuffer + Buffer,
146 >(&self, message: &mut DnsMessage<PTR_STORAGE, DNS_SECTION, B>) -> Result<usize, DnsMessageError> {
147 message.write_bytes(self.data)
148 }
149}
150
151#[derive(Copy, Clone, Debug)]
153pub struct TxtIterator<'a> {
154 data: &'a [u8],
155 pos: usize,
156}
157
158impl<'a> Iterator for TxtIterator<'a> {
159 type Item = Result<&'a [u8], DnsMessageError>;
160
161 fn next(&mut self) -> Option<Self::Item> {
162 if self.pos >= self.data.len() {
163 return None;
164 }
165
166 let len = self.data[self.pos] as usize;
167
168 if len == 0 && self.pos + 1 == self.data.len() {
169 return None;
170 } else if len == 0 {
171 return Some(Err(DnsMessageError::DnsError(DnsError::InvalidTxtRecord)));
172 }
173
174 self.pos += 1;
175
176 let end = self.pos + len;
177
178 if end > self.data.len() {
179 return Some(Err(DnsMessageError::DnsError(DnsError::InvalidTxtRecord)));
180 }
181
182 let result = &self.data[self.pos..end];
183 self.pos = end;
184
185 Some(Ok(result))
186 }
187}