use super::super::iana::OptionCode;
use super::super::message_builder::OptBuilder;
use super::super::wire::{Composer, ParseError};
use super::{ComposeOptData, Opt, OptData, ParseOptData};
use core::cmp::Ordering;
use core::convert::TryInto;
use core::{borrow, fmt, hash, mem};
use octseq::builder::OctetsBuilder;
use octseq::octets::{Octets, OctetsFrom};
use octseq::parse::Parser;
#[derive(Clone)]
#[repr(transparent)]
pub struct KeyTag<Octs: ?Sized> {
octets: Octs,
}
impl KeyTag<()> {
pub(super) const CODE: OptionCode = OptionCode::KEY_TAG;
}
impl<Octs> KeyTag<Octs> {
pub fn from_octets(octets: Octs) -> Result<Self, ParseError>
where
Octs: AsRef<[u8]>,
{
KeyTag::check_len(octets.as_ref().len())?;
Ok(unsafe { Self::from_octets_unchecked(octets) })
}
pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
Self { octets }
}
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
parser: &mut Parser<'a, Src>,
) -> Result<Self, ParseError> {
let len = parser.remaining();
KeyTag::check_len(len)?;
let octets = parser.parse_octets(len)?;
Ok(unsafe { Self::from_octets_unchecked(octets) })
}
}
impl KeyTag<[u8]> {
pub fn from_slice(slice: &[u8]) -> Result<&Self, ParseError> {
Self::check_len(slice.len())?;
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
#[must_use]
pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
mem::transmute(slice)
}
fn check_len(len: usize) -> Result<(), ParseError> {
if len > usize::from(u16::MAX) {
Err(ParseError::form_error("long edns-key-tag option"))
} else if len % 2 == 1 {
Err(ParseError::form_error("invalid edns-key-tag option length"))
} else {
Ok(())
}
}
}
impl<Octs: ?Sized> KeyTag<Octs> {
pub fn as_octets(&self) -> &Octs {
&self.octets
}
pub fn into_octets(self) -> Octs
where
Octs: Sized,
{
self.octets
}
pub fn as_slice(&self) -> &[u8]
where
Octs: AsRef<[u8]>,
{
self.octets.as_ref()
}
pub fn as_slice_mut(&mut self) -> &mut [u8]
where
Octs: AsMut<[u8]>,
{
self.octets.as_mut()
}
pub fn iter(&self) -> KeyTagIter<'_>
where
Octs: AsRef<[u8]>,
{
KeyTagIter(self.octets.as_ref())
}
}
impl<Octs, SrcOcts> OctetsFrom<KeyTag<SrcOcts>> for KeyTag<Octs>
where
Octs: OctetsFrom<SrcOcts>,
{
type Error = Octs::Error;
fn try_octets_from(src: KeyTag<SrcOcts>) -> Result<Self, Self::Error> {
Octs::try_octets_from(src.octets)
.map(|octets| unsafe { Self::from_octets_unchecked(octets) })
}
}
impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for KeyTag<Octs> {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octs: AsMut<[u8]> + ?Sized> AsMut<[u8]> for KeyTag<Octs> {
fn as_mut(&mut self) -> &mut [u8] {
self.as_slice_mut()
}
}
impl<Octs: AsRef<[u8]> + ?Sized> borrow::Borrow<[u8]> for KeyTag<Octs> {
fn borrow(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octs> borrow::BorrowMut<[u8]> for KeyTag<Octs>
where
Octs: AsMut<[u8]> + AsRef<[u8]> + ?Sized,
{
fn borrow_mut(&mut self) -> &mut [u8] {
self.as_slice_mut()
}
}
impl<Octs: ?Sized> OptData for KeyTag<Octs> {
fn code(&self) -> OptionCode {
OptionCode::KEY_TAG
}
}
impl<'a, Octs: Octets> ParseOptData<'a, Octs> for KeyTag<Octs::Range<'a>> {
fn parse_option(
code: OptionCode,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
if code == OptionCode::KEY_TAG {
Self::parse(parser).map(Some)
} else {
Ok(None)
}
}
}
impl<Octs: AsRef<[u8]> + ?Sized> ComposeOptData for KeyTag<Octs> {
fn compose_len(&self) -> u16 {
self.octets
.as_ref()
.len()
.try_into()
.expect("long option data")
}
fn compose_option<Target: OctetsBuilder + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
target.append_slice(self.octets.as_ref())
}
}
impl<'a, Octs: AsRef<[u8]> + ?Sized> IntoIterator for &'a KeyTag<Octs> {
type Item = u16;
type IntoIter = KeyTagIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Display for KeyTag<Octets> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for v in self.octets.as_ref() {
if first {
write!(f, "{:X}", ((*v as u16) << 8) | *v as u16)?;
first = false;
} else {
write!(f, ", {:X}", ((*v as u16) << 8) | *v as u16)?;
}
}
Ok(())
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Debug for KeyTag<Octets> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "KeyTag([{}])", self)
}
}
impl<Octs, Other> PartialEq<Other> for KeyTag<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
Other: AsRef<[u8]> + ?Sized,
{
fn eq(&self, other: &Other) -> bool {
self.as_slice().eq(other.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Eq for KeyTag<Octs> {}
impl<Octs, Other> PartialOrd<Other> for KeyTag<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
Other: AsRef<[u8]> + ?Sized,
{
fn partial_cmp(&self, other: &Other) -> Option<Ordering> {
self.as_slice().partial_cmp(other.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Ord for KeyTag<Octs> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for KeyTag<Octs> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state)
}
}
impl<Octs: Octets> Opt<Octs> {
pub fn key_tag(&self) -> Option<KeyTag<Octs::Range<'_>>> {
self.first()
}
}
impl<Target: Composer> OptBuilder<'_, Target> {
pub fn key_tag(
&mut self,
key_tag: &KeyTag<impl AsRef<[u8]> + ?Sized>,
) -> Result<(), Target::AppendError> {
self.push(key_tag)
}
}
#[cfg(feature = "serde")]
impl<Octs: AsRef<[u8]>> serde::Serialize for KeyTag<Octs> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut list = serializer.serialize_seq(None)?;
for i in self.as_ref().iter() {
list.serialize_element(i)?;
}
list.end()
}
}
#[derive(Clone, Copy, Debug)]
pub struct KeyTagIter<'a>(&'a [u8]);
impl Iterator for KeyTagIter<'_> {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
if self.0.len() < 2 {
None
} else {
let (item, tail) = self.0.split_at(2);
self.0 = tail;
Some(u16::from_be_bytes(item.try_into().unwrap()))
}
}
}
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
mod test {
use super::super::test::test_option_compose_parse;
use super::*;
#[test]
#[allow(clippy::redundant_closure)] fn nsid_compose_parse() {
test_option_compose_parse(
&KeyTag::from_octets("fooo").unwrap(),
|parser| KeyTag::parse(parser),
);
}
}