use crate::{
Buf, BufError, BufMut, BufResult, Codec, Cursor, ietf::igmp::Checksum, ietf::ipv4::Address,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum RecordType {
ModeIsInclude = 1,
ModeIsExclude = 2,
ChangeToIncludeMode = 3,
ChangeToExcludeMode = 4,
AllowNewSources = 5,
BlockOldSources = 6,
}
impl Codec for RecordType {
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::ModeIsInclude as u8) => Ok(Self::ModeIsInclude),
x if x == (Self::ModeIsExclude as u8) => Ok(Self::ModeIsExclude),
x if x == (Self::ChangeToIncludeMode as u8) => Ok(Self::ChangeToIncludeMode),
x if x == (Self::ChangeToExcludeMode as u8) => Ok(Self::ChangeToExcludeMode),
x if x == (Self::AllowNewSources as u8) => Ok(Self::AllowNewSources),
x if x == (Self::BlockOldSources as u8) => Ok(Self::BlockOldSources),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GroupRecord {
pub record_type: RecordType,
pub aux_data_len: u8,
pub multicast_address: Address,
pub source_addresses: Vec<Address>,
pub auxiliary_data: Vec<u8>,
}
impl Codec for GroupRecord {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.record_type.encode(writer, ())?;
self.aux_data_len.encode(writer, ())?;
(self.source_addresses.len() as u16).encode(writer, ())?;
let mc_bytes: [u8; 4] = self.multicast_address.into();
mc_bytes.encode(writer, ())?;
for src in &self.source_addresses {
let src_bytes: [u8; 4] = (*src).into();
src_bytes.encode(writer, ())?;
}
self.auxiliary_data.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let record_type = RecordType::decode(reader, ())?;
let aux_data_len = u8::decode(reader, ())?;
let num_sources = u16::decode(reader, ())?;
let multicast_address = Address::from(reader.read_array::<4>()?);
let mut source_addresses = Vec::with_capacity(num_sources as usize);
for _ in 0..num_sources {
source_addresses.push(Address::from(reader.read_array::<4>()?));
}
let aux_len_bytes = aux_data_len as usize * 4;
let mut auxiliary_data = vec![0u8; aux_len_bytes];
reader.read_into(&mut auxiliary_data)?;
Ok(Self {
record_type,
aux_data_len,
multicast_address,
source_addresses,
auxiliary_data,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Query {
pub max_response_code: u8,
pub checksum: Checksum,
pub group_address: Address,
pub s_flag: bool,
pub qrv: u8,
pub qqic: u8,
pub source_addresses: Vec<Address>,
}
impl Query {
pub const TYPE: u8 = 0x11;
}
impl Codec for Query {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.max_response_code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
let group_bytes: [u8; 4] = self.group_address.into();
group_bytes.encode(writer, ())?;
let s_qrv = (if self.s_flag { 1 << 3 } else { 0 }) | (self.qrv & 0x07);
s_qrv.encode(writer, ())?;
self.qqic.encode(writer, ())?;
(self.source_addresses.len() as u16).encode(writer, ())?;
for src in &self.source_addresses {
let src_bytes: [u8; 4] = (*src).into();
src_bytes.encode(writer, ())?;
}
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if u8::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let max_response_code = u8::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let group_address = Address::from(reader.read_array::<4>()?);
let s_qrv = u8::decode(reader, ())?;
let s_flag = (s_qrv & 0x08) != 0;
let qrv = s_qrv & 0x07;
let qqic = u8::decode(reader, ())?;
let num_sources = u16::decode(reader, ())?;
let mut source_addresses = Vec::with_capacity(num_sources as usize);
for _ in 0..num_sources {
source_addresses.push(Address::from(reader.read_array::<4>()?));
}
Ok(Self {
max_response_code,
checksum,
group_address,
s_flag,
qrv,
qqic,
source_addresses,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Report {
pub checksum: Checksum,
pub group_records: Vec<GroupRecord>,
}
impl Report {
pub const TYPE: u8 = 0x22;
}
impl Codec for Report {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
0u8.encode(writer, ())?; self.checksum.encode(writer, ())?;
0u16.encode(writer, ())?; (self.group_records.len() as u16).encode(writer, ())?;
for record in &self.group_records {
record.encode(writer, ())?;
}
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if u8::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let _reserved1 = u8::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let _reserved2 = u16::decode(reader, ())?;
let num_records = u16::decode(reader, ())?;
let mut group_records = Vec::with_capacity(num_records as usize);
for _ in 0..num_records {
group_records.push(GroupRecord::decode(reader, ())?);
}
Ok(Self {
checksum,
group_records,
})
}
}