use crate::{
Buf, BufError, BufMut, BufResult, Codec, Cursor,
ietf::ipv4::{Address, Header},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Message {
EchoReply(EchoReply),
DestinationUnreachable(DestinationUnreachable),
SourceQuench(SourceQuench),
Redirect(Redirect),
Echo(Echo),
TimeExceeded(TimeExceeded),
ParameterProblem(ParameterProblem),
Timestamp(Timestamp),
TimestampReply(TimestampReply),
InformationRequest(InformationRequest),
InformationReply(InformationReply),
}
impl Codec for Message {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
match self {
Message::EchoReply(message) => message.encode(writer, ()),
Message::DestinationUnreachable(message) => message.encode(writer, ()),
Message::SourceQuench(message) => message.encode(writer, ()),
Message::Redirect(message) => message.encode(writer, ()),
Message::Echo(message) => message.encode(writer, ()),
Message::TimeExceeded(message) => message.encode(writer, ()),
Message::ParameterProblem(message) => message.encode(writer, ()),
Message::Timestamp(message) => message.encode(writer, ()),
Message::TimestampReply(message) => message.encode(writer, ()),
Message::InformationRequest(message) => message.encode(writer, ()),
Message::InformationReply(message) => message.encode(writer, ()),
}
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let type_byte = reader.peek_u8()?;
let r#type = Type::decode(&mut Cursor::new(&[type_byte]), ())?;
Ok(match r#type {
Type::EchoReply => Self::EchoReply(EchoReply::decode(reader, ())?),
Type::DestinationUnreachable => {
Self::DestinationUnreachable(DestinationUnreachable::decode(reader, ())?)
}
Type::SourceQuench => Self::SourceQuench(SourceQuench::decode(reader, ())?),
Type::Redirect => Self::Redirect(Redirect::decode(reader, ())?),
Type::Echo => Self::Echo(Echo::decode(reader, ())?),
Type::TimeExceeded => Self::TimeExceeded(TimeExceeded::decode(reader, ())?),
Type::ParameterProblem => Self::ParameterProblem(ParameterProblem::decode(reader, ())?),
Type::Timestamp => Self::Timestamp(Timestamp::decode(reader, ())?),
Type::TimestampReply => Self::TimestampReply(TimestampReply::decode(reader, ())?),
Type::InformationRequest => {
Self::InformationRequest(InformationRequest::decode(reader, ())?)
}
Type::InformationReply => Self::InformationReply(InformationReply::decode(reader, ())?),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Type {
EchoReply = 0,
DestinationUnreachable = 3,
SourceQuench = 4,
Redirect = 5,
Echo = 8,
TimeExceeded = 11,
ParameterProblem = 12,
Timestamp = 13,
TimestampReply = 14,
InformationRequest = 15,
InformationReply = 16,
}
impl Codec for Type {
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> {
match u8::decode(reader, ())? {
x if x == (Self::EchoReply as u8) => Ok(Self::EchoReply),
x if x == (Self::DestinationUnreachable as u8) => Ok(Self::DestinationUnreachable),
x if x == (Self::SourceQuench as u8) => Ok(Self::SourceQuench),
x if x == (Self::Redirect as u8) => Ok(Self::Redirect),
x if x == (Self::Echo as u8) => Ok(Self::Echo),
x if x == (Self::TimeExceeded as u8) => Ok(Self::TimeExceeded),
x if x == (Self::ParameterProblem as u8) => Ok(Self::ParameterProblem),
x if x == (Self::Timestamp as u8) => Ok(Self::Timestamp),
x if x == (Self::TimestampReply as u8) => Ok(Self::TimestampReply),
x if x == (Self::InformationRequest as u8) => Ok(Self::InformationRequest),
x if x == (Self::InformationReply as u8) => Ok(Self::InformationReply),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum NoCode {
Zero = 0,
}
impl Codec for NoCode {
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> {
match u8::decode(reader, ())? {
x if x == Self::Zero as u8 => Ok(Self::Zero),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Checksum(pub u16);
impl Checksum {
pub fn calculate(data: &[u8]) -> Self {
let mut sum: u32 = 0;
let mut i = 0;
while i + 1 < data.len() {
sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
i += 2;
}
if i < data.len() {
sum += (data[i] as u32) << 8;
}
while sum >> 16 != 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
Checksum(!(sum as u16))
}
}
impl Codec for Checksum {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.0.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
Ok(Self(u16::decode(reader, ())?))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DestinationUnreachable {
pub code: DestinationUnreachableCode,
pub checksum: Checksum,
pub header: Header,
pub bytes: [u8; 8],
}
impl DestinationUnreachable {
pub const TYPE: Type = Type::DestinationUnreachable;
pub const UNUSED: [u8; 4] = [0u8; 4];
}
impl Codec for DestinationUnreachable {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.header.encode(writer, ())?;
self.bytes.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::DestinationUnreachable => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = DestinationUnreachableCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
match reader.read_array::<4>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let header = Header::decode(reader, ())?;
let bytes = reader.read_array::<8>()?;
Ok(Self {
code,
checksum,
header,
bytes,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum DestinationUnreachableCode {
NetUnreachable = 0,
HostUnreachable = 1,
ProtocolUnreachable = 2,
PortUnreachable = 3,
FragmentationNeededAndDfSet = 4,
SourceRouteFailed = 5,
}
impl Codec for DestinationUnreachableCode {
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> {
match u8::decode(reader, ())? {
0 => Ok(Self::NetUnreachable),
1 => Ok(Self::HostUnreachable),
2 => Ok(Self::ProtocolUnreachable),
3 => Ok(Self::PortUnreachable),
4 => Ok(Self::FragmentationNeededAndDfSet),
5 => Ok(Self::SourceRouteFailed),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimeExceeded {
pub code: TimeExceededCode,
pub checksum: Checksum,
pub header: Header,
pub bytes: [u8; 8],
}
impl TimeExceeded {
pub const TYPE: Type = Type::TimeExceeded;
pub const UNUSED: [u8; 4] = [0u8; 4];
}
impl Codec for TimeExceeded {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.header.encode(writer, ())?;
self.bytes.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::TimeExceeded => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = TimeExceededCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
match reader.read_array::<4>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let header = Header::decode(reader, ())?;
let bytes = reader.read_array::<8>()?;
Ok(Self {
code,
checksum,
header,
bytes,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum TimeExceededCode {
TimeToLiveExceededInTransit = 0,
FragmentReassemblyTimeExceeded = 1,
}
impl Codec for TimeExceededCode {
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> {
match u8::decode(reader, ())? {
0 => Ok(Self::TimeToLiveExceededInTransit),
1 => Ok(Self::FragmentReassemblyTimeExceeded),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParameterProblem {
pub code: ParameterProblemCode,
pub checksum: Checksum,
pub pointer: u8,
pub unused: [u8; 3],
pub header: Header,
pub bytes: [u8; 8],
}
impl ParameterProblem {
pub const TYPE: Type = Type::ParameterProblem;
pub const UNUSED: [u8; 3] = [0u8; 3];
}
impl Codec for ParameterProblem {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.pointer.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.header.encode(writer, ())?;
self.bytes.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::ParameterProblem => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = ParameterProblemCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let pointer = u8::decode(reader, ())?;
match reader.read_array::<3>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let header = Header::decode(reader, ())?;
let bytes = reader.read_array::<8>()?;
Ok(Self {
code,
checksum,
pointer,
unused: Self::UNUSED,
header,
bytes,
})
}
}
pub type ParameterProblemCode = NoCode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceQuench {
pub code: SourceQuenchCode,
pub checksum: Checksum,
pub header: Header,
pub bytes: [u8; 8],
}
impl SourceQuench {
pub const TYPE: Type = Type::SourceQuench;
pub const UNUSED: [u8; 4] = [0u8; 4];
}
impl Codec for SourceQuench {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.header.encode(writer, ())?;
self.bytes.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::SourceQuench => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = SourceQuenchCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
match reader.read_array::<4>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let header = Header::decode(reader, ())?;
let bytes = reader.read_array::<8>()?;
Ok(Self {
code,
checksum,
header,
bytes,
})
}
}
pub type SourceQuenchCode = NoCode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Redirect {
pub code: RedirectCode,
pub checksum: Checksum,
pub gateway_internet_address: Address,
pub header: Header,
pub bytes: [u8; 8],
}
impl Redirect {
pub const TYPE: Type = Type::Redirect;
}
impl Codec for Redirect {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.gateway_internet_address.encode(writer, ())?;
self.header.encode(writer, ())?;
self.bytes.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::Redirect => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = RedirectCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let gateway_internet_address = Address::decode(reader, ())?;
let header = Header::decode(reader, ())?;
let bytes = reader.read_array::<8>()?;
Ok(Self {
code,
checksum,
gateway_internet_address,
header,
bytes,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum RedirectCode {
Network = 0,
Host = 1,
TypeOfServiceAndNetwork = 2,
TypeOfServiceAndHost = 3,
}
impl Codec for RedirectCode {
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> {
match u8::decode(reader, ())? {
0 => Ok(Self::Network),
1 => Ok(Self::Host),
2 => Ok(Self::TypeOfServiceAndNetwork),
3 => Ok(Self::TypeOfServiceAndHost),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Echo {
pub code: EchoCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub data: Vec<u8>,
}
impl Echo {
pub const TYPE: Type = Type::Echo;
}
impl Codec for Echo {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::Echo => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = EchoCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
data,
})
}
}
pub type EchoCode = NoCode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EchoReply {
pub code: EchoReplyCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub data: Vec<u8>,
}
impl EchoReply {
pub const TYPE: Type = Type::EchoReply;
}
impl Codec for EchoReply {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::EchoReply => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = EchoReplyCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
data,
})
}
}
pub type EchoReplyCode = NoCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Timestamp {
pub code: TimestampCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub originate_timestamp: u32,
pub receive_timestamp: u32,
pub transmit_timestamp: u32,
}
impl Timestamp {
pub const TYPE: Type = Type::Timestamp;
}
impl Codec for Timestamp {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.originate_timestamp.encode(writer, ())?;
self.receive_timestamp.encode(writer, ())?;
self.transmit_timestamp.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::Timestamp => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = TimestampCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let originate_timestamp = u32::decode(reader, ())?;
let receive_timestamp = u32::decode(reader, ())?;
let transmit_timestamp = u32::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
originate_timestamp,
receive_timestamp,
transmit_timestamp,
})
}
}
pub type TimestampCode = NoCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TimestampReply {
pub code: TimestampReplyCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub originate_timestamp: u32,
pub receive_timestamp: u32,
pub transmit_timestamp: u32,
}
impl TimestampReply {
pub const TYPE: Type = Type::TimestampReply;
}
impl Codec for TimestampReply {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.originate_timestamp.encode(writer, ())?;
self.receive_timestamp.encode(writer, ())?;
self.transmit_timestamp.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::TimestampReply => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = TimestampReplyCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let originate_timestamp = u32::decode(reader, ())?;
let receive_timestamp = u32::decode(reader, ())?;
let transmit_timestamp = u32::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
originate_timestamp,
receive_timestamp,
transmit_timestamp,
})
}
}
pub type TimestampReplyCode = NoCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct InformationRequest {
pub code: InformationRequestCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
}
impl InformationRequest {
pub const TYPE: Type = Type::InformationRequest;
}
impl Codec for InformationRequest {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::InformationRequest => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = InformationRequestCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
})
}
}
pub type InformationRequestCode = NoCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct InformationReply {
pub code: InformationReplyCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
}
impl InformationReply {
pub const TYPE: Type = Type::InformationReply;
}
impl Codec for InformationReply {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::InformationReply => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = InformationReplyCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
})
}
}
pub type InformationReplyCode = NoCode;
#[cfg(test)]
mod tests {
use core::fmt::Debug;
use crate::ietf::ip::Protocol;
use crate::ietf::ipv4::{self, Address, Dscp, Ecn, Header, Ihl, Options};
use crate::{
Codec, Cursor,
ietf::icmpv4::{
Checksum, DestinationUnreachable, DestinationUnreachableCode, Echo, EchoCode,
EchoReply, EchoReplyCode, InformationReply, InformationReplyCode, InformationRequest,
InformationRequestCode, NoCode, ParameterProblem, ParameterProblemCode, Redirect,
RedirectCode, SourceQuench, SourceQuenchCode, TimeExceeded, TimeExceededCode,
Timestamp, TimestampCode, TimestampReply, TimestampReplyCode, Type,
},
};
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 checksum() {
let packet = &[0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02];
let checksum = Checksum::calculate(packet);
let mut with_checksum = vec![0x08, 0x00];
with_checksum.extend_from_slice(&checksum.0.to_be_bytes());
with_checksum.extend_from_slice(&[0x00, 0x01, 0x00, 0x02]);
let verify = Checksum::calculate(&with_checksum);
assert!(verify.0 == 0x0000 || verify.0 == 0xFFFF);
}
#[test]
fn type_roundtrip() {
let cases = [
(Type::EchoReply, &[0x00][..]),
(Type::DestinationUnreachable, &[0x03][..]),
(Type::SourceQuench, &[0x04][..]),
(Type::Redirect, &[0x05][..]),
(Type::Echo, &[0x08][..]),
(Type::TimeExceeded, &[0x0b][..]),
(Type::ParameterProblem, &[0x0c][..]),
(Type::Timestamp, &[0x0d][..]),
(Type::TimestampReply, &[0x0e][..]),
(Type::InformationRequest, &[0x0f][..]),
(Type::InformationReply, &[0x10][..]),
];
for (t, bytes) in &cases {
codec_roundtrip(*t, bytes, ());
}
}
#[test]
fn no_code() {
codec_roundtrip(NoCode::Zero, &[0x00], ());
}
#[test]
fn destination_unreachable() {
let etalon_bytes = &[
0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let etalon_struct = DestinationUnreachable {
code: DestinationUnreachableCode::HostUnreachable,
checksum: Checksum(0x0000),
header: Header {
ihl: Ihl(5),
dscp: Dscp::new(0).unwrap(),
ecn: Ecn::NonEct,
total_length: 8,
identification: 0,
df: true,
mf: false,
fragment_offset: 0,
ttl: 64,
protocol: Protocol::UDP,
checksum: ipv4::Checksum(0),
source_address: Address::LOOPBACK,
destination_address: Address::LOOPBACK,
options: Options::default(),
},
bytes: [0x00; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn source_quench() {
let etalon_bytes = &[
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let etalon_struct = SourceQuench {
code: SourceQuenchCode::Zero,
checksum: Checksum(0x0000),
header: Header {
ihl: Ihl(5),
dscp: Dscp::new(0).unwrap(),
ecn: Ecn::NonEct,
total_length: 8,
identification: 0,
df: true,
mf: false,
fragment_offset: 0,
ttl: 64,
protocol: Protocol::UDP,
checksum: ipv4::Checksum(0),
source_address: Address::LOOPBACK,
destination_address: Address::LOOPBACK,
options: Options::default(),
},
bytes: [0x00; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn redirect() {
let etalon_bytes = &[
0x05, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let etalon_struct = Redirect {
code: RedirectCode::Network,
checksum: Checksum(0x0000),
gateway_internet_address: Address::from([10, 0, 0, 1]),
header: Header {
ihl: Ihl(5),
dscp: Dscp::new(0).unwrap(),
ecn: Ecn::NonEct,
total_length: 8,
identification: 0,
df: true,
mf: false,
fragment_offset: 0,
ttl: 64,
protocol: Protocol::UDP,
checksum: ipv4::Checksum(0),
source_address: Address::LOOPBACK,
destination_address: Address::LOOPBACK,
options: Options::default(),
},
bytes: [0x00; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn time_exceeded() {
let etalon_bytes = &[
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let etalon_struct = TimeExceeded {
code: TimeExceededCode::TimeToLiveExceededInTransit,
checksum: Checksum(0x0000),
header: Header {
ihl: Ihl(5),
dscp: Dscp::new(0).unwrap(),
ecn: Ecn::NonEct,
total_length: 8,
identification: 0,
df: true,
mf: false,
fragment_offset: 0,
ttl: 64,
protocol: Protocol::UDP,
checksum: ipv4::Checksum(0),
source_address: Address::LOOPBACK,
destination_address: Address::LOOPBACK,
options: Options::default(),
},
bytes: [0x00; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn parameter_problem() {
let etalon_bytes = &[
0x0c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let etalon_struct = ParameterProblem {
code: ParameterProblemCode::Zero,
checksum: Checksum(0x0000),
pointer: 5,
unused: ParameterProblem::UNUSED,
header: Header {
ihl: Ihl(5),
dscp: Dscp::new(0).unwrap(),
ecn: Ecn::NonEct,
total_length: 8,
identification: 0,
df: true,
mf: false,
fragment_offset: 0,
ttl: 64,
protocol: Protocol::UDP,
checksum: ipv4::Checksum(0),
source_address: Address::LOOPBACK,
destination_address: Address::LOOPBACK,
options: Options::default(),
},
bytes: [0x00; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo() {
let etalon_bytes = &[
0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x41, 0x42, 0x43, ];
let etalon_struct = Echo {
code: EchoCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
data: vec![0x41, 0x42, 0x43],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo_reply() {
let etalon_bytes = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x41, 0x42, 0x43, ];
let etalon_struct = EchoReply {
code: EchoReplyCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
data: vec![0x41, 0x42, 0x43],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn timestamp() {
let etalon_bytes = &[
0x0d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, ];
let etalon_struct = Timestamp {
code: TimestampCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
originate_timestamp: 3,
receive_timestamp: 4,
transmit_timestamp: 5,
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn timestamp_reply() {
let etalon_bytes = &[
0x0e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, ];
let etalon_struct = TimestampReply {
code: TimestampReplyCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
originate_timestamp: 3,
receive_timestamp: 4,
transmit_timestamp: 5,
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn information_request() {
let etalon_bytes = &[
0x0f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, ];
let etalon_struct = InformationRequest {
code: InformationRequestCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn information_reply() {
let etalon_bytes = &[
0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, ];
let etalon_struct = InformationReply {
code: InformationReplyCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
}