use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Option {
EndOfOptionsList(EndOfOptionsList),
NoOperation(NoOperation),
AdditionalPayloadChecksum(AdditionalPayloadChecksum),
Fragmentation(Fragmentation),
MaximumDatagramSize(MaximumDatagramSize),
MaximumReassembledDatagramSize(MaximumReassembledDatagramSize),
Request(EchoRequest),
Response(EchoResponse),
Timestamp(Timestamp),
}
impl Codec for Option {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
match self {
Option::EndOfOptionsList(opt) => opt.encode(writer, ()),
Option::NoOperation(opt) => opt.encode(writer, ()),
Option::AdditionalPayloadChecksum(opt) => opt.encode(writer, ()),
Option::Fragmentation(opt) => opt.encode(writer, ()),
Option::MaximumDatagramSize(opt) => opt.encode(writer, ()),
Option::MaximumReassembledDatagramSize(opt) => opt.encode(writer, ()),
Option::Request(opt) => opt.encode(writer, ()),
Option::Response(opt) => opt.encode(writer, ()),
Option::Timestamp(opt) => opt.encode(writer, ()),
}
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let kind = Kind::decode(reader, ())?;
match kind {
Kind::EndOfOptionList => Ok(Option::EndOfOptionsList(EndOfOptionsList::decode(
reader,
(),
)?)),
Kind::NoOperation => Ok(Option::NoOperation(NoOperation::decode(reader, ())?)),
Kind::AdditionalPayloadChecksum => Ok(Option::AdditionalPayloadChecksum(
AdditionalPayloadChecksum::decode(reader, ())?,
)),
Kind::Fragmentation => Ok(Option::Fragmentation(Fragmentation::decode(reader, ())?)),
Kind::MaximumDatagramSize => Ok(Option::MaximumDatagramSize(
MaximumDatagramSize::decode(reader, ())?,
)),
Kind::MaximumReassembledDatagramSize => Ok(Option::MaximumReassembledDatagramSize(
MaximumReassembledDatagramSize::decode(reader, ())?,
)),
Kind::Request => Ok(Option::Request(EchoRequest::decode(reader, ())?)),
Kind::Response => Ok(Option::Response(EchoResponse::decode(reader, ())?)),
Kind::Timestamp => Ok(Option::Timestamp(Timestamp::decode(reader, ())?)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Kind {
EndOfOptionList = 0,
NoOperation = 1,
AdditionalPayloadChecksum = 2,
Fragmentation = 3,
MaximumDatagramSize = 4,
MaximumReassembledDatagramSize = 5,
Request = 6,
Response = 7,
Timestamp = 8,
}
impl TryFrom<u8> for Kind {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Kind::EndOfOptionList),
1 => Ok(Kind::NoOperation),
2 => Ok(Kind::AdditionalPayloadChecksum),
3 => Ok(Kind::Fragmentation),
4 => Ok(Kind::MaximumDatagramSize),
5 => Ok(Kind::MaximumReassembledDatagramSize),
6 => Ok(Kind::Request),
7 => Ok(Kind::Response),
8 => Ok(Kind::Timestamp),
_ => Err(()),
}
}
}
impl Codec for Kind {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let byte = u8::decode(reader, ())?;
Self::try_from(byte).map_err(|_| BufError::UnexpectedValue)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EndOfOptionsList;
impl EndOfOptionsList {
pub const KIND: Kind = Kind::EndOfOptionList;
}
impl Codec for EndOfOptionsList {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
Ok(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NoOperation;
impl NoOperation {
pub const KIND: Kind = Kind::NoOperation;
}
impl Codec for NoOperation {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
Ok(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AdditionalPayloadChecksum {
pub crc32c: u32,
}
impl AdditionalPayloadChecksum {
pub const KIND: Kind = Kind::AdditionalPayloadChecksum;
pub const LEN: u8 = 8;
}
impl Codec for AdditionalPayloadChecksum {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
Self::LEN.encode(writer, ())?;
0u16.encode(writer, ())?;
self.crc32c.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
if u8::decode(reader, ())? != Self::LEN {
return Err(BufError::UnexpectedValue);
}
let _reserved = u16::decode(reader, ())?;
let crc32c = u32::decode(reader, ())?;
Ok(Self { crc32c })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Fragmentation {
pub len: u8,
}
impl Fragmentation {
pub const KIND: Kind = Kind::Fragmentation;
}
impl Codec for Fragmentation {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
self.len.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
let len = u8::decode(reader, ())?;
Ok(Self { len })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaximumDatagramSize {
pub mds: u16,
}
impl MaximumDatagramSize {
pub const KIND: Kind = Kind::MaximumDatagramSize;
pub const LEN: u8 = 4;
}
impl Codec for MaximumDatagramSize {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
Self::LEN.encode(writer, ())?;
self.mds.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
if u8::decode(reader, ())? != Self::LEN {
return Err(BufError::UnexpectedValue);
}
let mds = u16::decode(reader, ())?;
Ok(Self { mds })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaximumReassembledDatagramSize {
pub mrds: u16,
}
impl MaximumReassembledDatagramSize {
pub const KIND: Kind = Kind::MaximumReassembledDatagramSize;
pub const LEN: u8 = 4;
}
impl Codec for MaximumReassembledDatagramSize {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
Self::LEN.encode(writer, ())?;
self.mrds.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
if u8::decode(reader, ())? != Self::LEN {
return Err(BufError::UnexpectedValue);
}
let mrds = u16::decode(reader, ())?;
Ok(Self { mrds })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EchoRequest {
pub mss: u16,
}
impl EchoRequest {
pub const KIND: Kind = Kind::Request;
pub const LEN: u8 = 4;
}
impl Codec for EchoRequest {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
Self::LEN.encode(writer, ())?;
self.mss.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
if u8::decode(reader, ())? != Self::LEN {
return Err(BufError::UnexpectedValue);
}
let mss = u16::decode(reader, ())?;
Ok(Self { mss })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EchoResponse {
pub len: u8,
}
impl EchoResponse {
pub const KIND: Kind = Kind::Response;
}
impl Codec for EchoResponse {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
self.len.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
let len = u8::decode(reader, ())?;
Ok(Self { len })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp {
pub ts_val: u32,
pub ts_ecr: u32,
}
impl Timestamp {
pub const KIND: Kind = Kind::Timestamp;
pub const LEN: u8 = 12;
}
impl Codec for Timestamp {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::KIND.encode(writer, ())?;
Self::LEN.encode(writer, ())?;
0u16.encode(writer, ())?;
self.ts_val.encode(writer, ())?;
self.ts_ecr.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Kind::decode(reader, ())? != Self::KIND {
return Err(BufError::UnexpectedValue);
}
if u8::decode(reader, ())? != Self::LEN {
return Err(BufError::UnexpectedValue);
}
let _reserved = u16::decode(reader, ())?;
let ts_val = u32::decode(reader, ())?;
let ts_ecr = u32::decode(reader, ())?;
Ok(Self { ts_val, ts_ecr })
}
}
#[cfg(test)]
mod tests {
use core::fmt::Debug;
use crate::{
Codec, Cursor,
udp::{Checksum, Header, Port},
};
fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
etalon_struct: T,
etalon_bytes: &[u8],
context: C,
) {
let mut encoded_bytes = vec![];
{
let writer = &mut Cursor::new(&mut encoded_bytes);
etalon_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
let decoded_struct = {
let reader = &mut Cursor::new(&mut encoded_bytes);
T::decode(reader, context).unwrap()
};
assert_eq!(etalon_struct, decoded_struct);
encoded_bytes.fill(0x00);
{
let writer = &mut Cursor::new(&mut encoded_bytes);
decoded_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
}
#[test]
fn end_of_options_list() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn no_operation() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn additional_payload_checksum() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn fragmentation() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn maximum_datagram_size() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn maximum_reassembled_datagram_size() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo_request() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo_response() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn timestamp() {
let etalon_bytes = &[
0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x00, 0x00, ];
let etalon_struct = Header {
source_port: Port::DNS,
destination_port: Port::from(50000u16),
length: 16,
checksum: Checksum(0),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
}