domain/base/opt/
keytag.rs
1use super::super::iana::OptionCode;
12use super::super::message_builder::OptBuilder;
13use super::super::wire::{Composer, ParseError};
14use super::{Opt, OptData, ComposeOptData, ParseOptData};
15use octseq::builder::OctetsBuilder;
16use octseq::octets::Octets;
17use octseq::parse::Parser;
18use core::{borrow, fmt, hash};
19use core::cmp::Ordering;
20use core::convert::TryInto;
21
22
23#[derive(Clone)]
31pub struct KeyTag<Octs: ?Sized> {
32 octets: Octs,
33}
34
35impl<Octs> KeyTag<Octs> {
36 pub fn from_octets(octets: Octs) -> Result<Self, ParseError>
42 where Octs: AsRef<[u8]> {
43 KeyTag::check_len(octets.as_ref().len())?;
44 Ok(unsafe { Self::from_octets_unchecked(octets ) })
45 }
46
47 pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
55 Self { octets }
56 }
57
58 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
59 parser: &mut Parser<'a, Src>
60 ) -> Result<Self, ParseError> {
61 let len = parser.remaining();
62 KeyTag::check_len(len)?;
63 let octets = parser.parse_octets(len)?;
64 Ok(unsafe { Self::from_octets_unchecked(octets) })
65 }
66}
67
68impl KeyTag<[u8]> {
69 pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
73 Self::check_len(slice.len())?;
74 Ok(unsafe { Self::from_slice_unchecked(slice) })
75 }
76
77 #[must_use]
85 pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
86 &*(slice as *const [u8] as *const Self)
87 }
88
89 fn check_len(len: usize) -> Result<(), ParseError> {
91 if len > usize::from(u16::MAX) {
92 Err(ParseError::form_error("long edns-key-tag option"))
93 }
94 else if len % 2 == 1 {
95 Err(ParseError::form_error("invalid edns-key-tag option length"))
96 }
97 else {
98 Ok(())
99 }
100 }
101}
102
103impl<Octs: ?Sized> KeyTag<Octs> {
104 pub fn as_octets(&self) -> &Octs {
109 &self.octets
110 }
111
112 pub fn into_octets(self) -> Octs
117 where
118 Octs: Sized,
119 {
120 self.octets
121 }
122
123 pub fn as_slice(&self) -> &[u8]
128 where
129 Octs: AsRef<[u8]>,
130 {
131 self.octets.as_ref()
132 }
133
134 pub fn as_slice_mut(&mut self) -> &mut [u8]
139 where
140 Octs: AsMut<[u8]>,
141 {
142 self.octets.as_mut()
143 }
144
145 pub fn iter(&self) -> KeyTagIter
147 where Octs: AsRef<[u8]> {
148 KeyTagIter(self.octets.as_ref())
149 }
150}
151
152impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for KeyTag<Octs> {
155 fn as_ref(&self) -> &[u8] {
156 self.as_slice()
157 }
158}
159
160impl<Octs: AsMut<[u8]> + ?Sized> AsMut<[u8]> for KeyTag<Octs> {
161 fn as_mut(&mut self) -> &mut [u8] {
162 self.as_slice_mut()
163 }
164}
165
166impl<Octs: AsRef<[u8]> + ?Sized> borrow::Borrow<[u8]> for KeyTag<Octs> {
167 fn borrow(&self) -> &[u8] {
168 self.as_slice()
169 }
170}
171
172impl<Octs> borrow::BorrowMut<[u8]> for KeyTag<Octs>
173where
174 Octs: AsMut<[u8]> + AsRef<[u8]> + ?Sized
175{
176 fn borrow_mut(&mut self) -> &mut [u8] {
177 self.as_slice_mut()
178 }
179}
180
181impl<Octs: ?Sized> OptData for KeyTag<Octs> {
184 fn code(&self) -> OptionCode {
185 OptionCode::KeyTag
186 }
187}
188
189impl<'a, Octs: Octets> ParseOptData<'a, Octs> for KeyTag<Octs::Range<'a>> {
190 fn parse_option(
191 code: OptionCode,
192 parser: &mut Parser<'a, Octs>,
193 ) -> Result<Option<Self>, ParseError> {
194 if code == OptionCode::KeyTag {
195 Self::parse(parser).map(Some)
196 }
197 else {
198 Ok(None)
199 }
200 }
201}
202
203impl<Octs: AsRef<[u8]> + ?Sized> ComposeOptData for KeyTag<Octs> {
204 fn compose_len(&self) -> u16 {
205 self.octets.as_ref().len().try_into().expect("long option data")
206 }
207
208 fn compose_option<Target: OctetsBuilder + ?Sized>(
209 &self, target: &mut Target
210 ) -> Result<(), Target::AppendError> {
211 target.append_slice(self.octets.as_ref())
212 }
213}
214
215
216impl<'a, Octs: AsRef<[u8]> + ?Sized> IntoIterator for &'a KeyTag<Octs> {
219 type Item = u16;
220 type IntoIter = KeyTagIter<'a>;
221
222 fn into_iter(self) -> Self::IntoIter {
223 self.iter()
224 }
225}
226
227
228impl<Octets: AsRef<[u8]> + ?Sized> fmt::Display for KeyTag<Octets> {
231 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232 let mut first = true;
233
234 for v in self.octets.as_ref() {
235 if first {
236 write!(f, "{:X}", ((*v as u16) << 8) | *v as u16)?;
237 first = false;
238 } else {
239 write!(f, ", {:X}", ((*v as u16) << 8) | *v as u16)?;
240 }
241 }
242
243 Ok(())
244 }
245}
246
247impl<Octets: AsRef<[u8]> + ?Sized> fmt::Debug for KeyTag<Octets> {
248 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249 write!(f, "KeyTag([{}])", self)
250 }
251}
252
253
254impl<Octs, Other> PartialEq<Other> for KeyTag<Octs>
257where
258 Octs: AsRef<[u8]> + ?Sized,
259 Other: AsRef<[u8]> + ?Sized,
260{
261 fn eq(&self, other: &Other) -> bool {
262 self.as_slice().eq(other.as_ref())
263 }
264}
265
266impl<Octs: AsRef<[u8]> + ?Sized> Eq for KeyTag<Octs> { }
267
268impl<Octs, Other> PartialOrd<Other> for KeyTag<Octs>
271where
272 Octs: AsRef<[u8]> + ?Sized,
273 Other: AsRef<[u8]> + ?Sized,
274{
275 fn partial_cmp(&self, other: &Other) -> Option<Ordering> {
276 self.as_slice().partial_cmp(other.as_ref())
277 }
278}
279
280impl<Octs: AsRef<[u8]> + ?Sized> Ord for KeyTag<Octs> {
281 fn cmp(&self, other: &Self) -> Ordering {
282 self.as_slice().cmp(other.as_slice())
283 }
284}
285
286impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for KeyTag<Octs> {
289 fn hash<H: hash::Hasher>(&self, state: &mut H) {
290 self.as_slice().hash(state)
291 }
292}
293
294impl<Octs: Octets> Opt<Octs> {
297 pub fn key_tag(&self) -> Option<KeyTag<Octs::Range<'_>>> {
302 self.first()
303 }
304}
305
306impl<'a, Target: Composer> OptBuilder<'a, Target> {
307 pub fn key_tag(
312 &mut self, key_tag: &KeyTag<impl AsRef<[u8]> + ?Sized>,
313 ) -> Result<(), Target::AppendError> {
314 self.push(key_tag)
315 }
316}
317
318
319#[derive(Clone, Copy, Debug)]
326pub struct KeyTagIter<'a>(&'a [u8]);
327
328impl<'a> Iterator for KeyTagIter<'a> {
329 type Item = u16;
330
331 fn next(&mut self) -> Option<Self::Item> {
332 if self.0.len() < 2 {
333 None
334 }
335 else {
336 let (item, tail) = self.0.split_at(2);
337 self.0 = tail;
338 Some(u16::from_be_bytes(item.try_into().unwrap()))
339 }
340 }
341}
342
343
344#[cfg(test)]
347#[cfg(all(feature = "std", feature = "bytes"))]
348mod test {
349 use super::*;
350 use super::super::test::test_option_compose_parse;
351
352 #[test]
353 #[allow(clippy::redundant_closure)] fn nsid_compose_parse() {
355 test_option_compose_parse(
356 &KeyTag::from_octets("fooo").unwrap(),
357 |parser| KeyTag::parse(parser)
358 );
359 }
360}
361