mod address;
mod address_list;
mod file;
mod max_message;
mod mtypes;
mod opaque;
mod overload_options;
mod sname;
mod string;
mod time;
mod time_offset;
pub use self::address::*;
pub use self::address_list::*;
pub use self::file::*;
pub use self::max_message::*;
pub use self::mtypes::*;
pub use self::opaque::*;
pub use self::overload_options::*;
pub use self::sname::*;
pub use self::string::*;
pub use self::time::*;
pub use self::time_offset::*;
use super::super::Slicer;
use log::{trace, warn};
use serde::{Deserialize, Serialize};
pub const MAGIC_SIZE: usize = 4;
pub const NON_MAGIC_SIZE: usize = 308;
pub const MIN_OPTIONS_SIZE: usize = MAGIC_SIZE + NON_MAGIC_SIZE;
pub type MagicBuffer = [u8; MAGIC_SIZE];
pub const MAGIC: MagicBuffer = [99, 130, 83, 99];
pub type OptionsBuffer = [u8; NON_MAGIC_SIZE];
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub enum MessageOptions {
Pad,
Netmask(AddressOption),
TimeOffset(TimeOffset),
Router(AddressListOption),
DnsServer(AddressListOption),
HostName(StringOption),
DomainName(StringOption),
Broadcast(AddressOption),
RequestedIp(AddressOption),
LeaseTime(TimeOption),
Overload(OverloadOptions),
MessageType(MessageTypes),
ServerIdentifier(AddressOption),
RequestedOptions(Vec<u8>),
Message(StringOption),
MaxMessage(MaxMessage),
T1(TimeOption),
T2(TimeOption),
VendorId(OpaqueOption),
ClientId(OpaqueOption),
SName(SNameOption),
FileName(FileOption),
RapidCommit,
SubnetSelect(AddressOption),
End,
Other(u8, u8, Vec<u8>),
}
impl MessageOptions {
pub fn to_tag(&self) -> u8 {
match self {
Self::Pad => 0,
Self::Netmask(_) => 1,
Self::TimeOffset(_) => 2,
Self::Router(_) => 3,
Self::DnsServer(_) => 6,
Self::HostName(_) => 12,
Self::DomainName(_) => 15,
Self::Broadcast(_) => 28,
Self::RequestedIp(_) => 50,
Self::LeaseTime(_) => 51,
Self::Overload(_) => 52,
Self::MessageType(_) => 53,
Self::ServerIdentifier(_) => 54,
Self::RequestedOptions(_) => 55,
Self::Message(_) => 56,
Self::MaxMessage(_) => 57,
Self::T1(_) => 58,
Self::T2(_) => 59,
Self::VendorId(_) => 60,
Self::ClientId(_) => 61,
Self::SName(_) => 66,
Self::FileName(_) => 67,
Self::RapidCommit => 80,
Self::SubnetSelect(_) => 118,
Self::End => 255,
Self::Other(x, _, _) => *x,
}
}
fn from_tag_value(tag: u8, value: &[u8]) -> Self {
match tag {
1 => Self::Netmask(value.into()),
2 => Self::TimeOffset(value.into()),
3 => Self::Router(value.into()),
6 => Self::DnsServer(value.into()),
12 => Self::HostName(value.into()),
15 => Self::DomainName(value.into()),
28 => Self::Broadcast(value.into()),
50 => Self::RequestedIp(value.into()),
51 => Self::LeaseTime(value.into()),
52 => Self::Overload(value.into()),
53 => Self::MessageType(MessageTypes::from(value)),
54 => Self::ServerIdentifier(value.into()),
55 => Self::RequestedOptions(value.into()),
56 => Self::Message(value.into()),
57 => Self::MaxMessage(value.into()),
58 => Self::T1(value.into()),
59 => Self::T2(value.into()),
60 => Self::VendorId(value.into()),
61 => Self::ClientId(value.into()),
66 => Self::SName(value.into()),
67 => Self::FileName(value.into()),
118 => Self::SubnetSelect(value.into()),
_ => Self::Other(tag, value.len() as u8, value.into()),
}
}
#[inline]
pub fn extend_into(&self, bytes: &mut Vec<u8>) {
match self {
Self::Pad => bytes.push(0),
Self::End => {
warn!("The End option is automatically handled during encode/decode. Don't use it.")
}
Self::RapidCommit => bytes.push(80),
Self::LeaseTime(x) => x.extend_into(bytes, 51),
Self::T1(x) => x.extend_into(bytes, 58),
Self::T2(x) => x.extend_into(bytes, 59),
Self::RequestedIp(x) => x.extend_into(bytes, 50),
Self::ServerIdentifier(x) => x.extend_into(bytes, 54),
Self::MessageType(x) => x.extend_into(bytes, 53),
Self::Netmask(x) => x.extend_into(bytes, 1),
Self::Broadcast(x) => x.extend_into(bytes, 28),
Self::Overload(x) => x.extend_into(bytes, 52),
Self::SName(x) => x.extend_into(bytes, 66),
Self::FileName(x) => x.extend_into(bytes, 67),
Self::Router(x) => x.extend_into(bytes, 3),
Self::DnsServer(x) => x.extend_into(bytes, 6),
Self::MaxMessage(x) => x.extend_into(bytes, 57),
Self::HostName(x) => x.extend_into(bytes, 12),
Self::DomainName(x) => x.extend_into(bytes, 15),
Self::Message(x) => x.extend_into(bytes, 56),
Self::SubnetSelect(x) => x.extend_into(bytes, 118),
Self::VendorId(x) => x.extend_into(bytes, 60),
Self::ClientId(x) => x.extend_into(bytes, 61),
Self::TimeOffset(x) => x.extend_into(bytes, 2),
Self::RequestedOptions(v) => {
bytes.push(55);
bytes.push(v.len() as u8);
bytes.extend(v.iter());
}
Self::Other(t, l, v) => {
bytes.push(*t);
bytes.push(*l);
bytes.extend(v);
}
};
}
}
#[derive(Debug, PartialEq)]
pub struct MessageOptionsList(Vec<MessageOptions>);
impl MessageOptionsList {
#[inline]
pub fn find_option(&self, tag: u8) -> Option<&MessageOptions> {
self.0.iter().find(|&option| option.to_tag() == tag)
}
#[inline]
pub fn add_option(&mut self, option: MessageOptions) {
self.0.push(option);
}
#[deprecated]
pub fn get(&self, index: usize) -> Option<&MessageOptions> {
self.0.get(index)
}
#[deprecated]
pub fn set(&mut self, index: usize, option: MessageOptions) {
self.0[index] = option;
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> Self {
let mut slicer = Slicer::new(bytes);
let mut options: Vec<MessageOptions> = Vec::with_capacity(NON_MAGIC_SIZE);
while let Some(tag) = slicer.slice(1) {
trace!("parsing option {:?}", tag);
match tag[0] {
0 => {} 80 => {
options.push(MessageOptions::RapidCommit);
}
255 => {
break; }
tag_with_length => {
match slicer.slice(1) {
Some(length) => match slicer.slice(length[0] as usize) {
Some(value) => {
options.push(MessageOptions::from_tag_value(tag_with_length, value))
}
None => break, },
None => break, }
}
}
}
Self(options)
}
}
impl From<Vec<MessageOptions>> for MessageOptionsList {
fn from(options: Vec<MessageOptions>) -> Self {
Self(options)
}
}
impl From<&MessageOptionsList> for Vec<u8> {
#[inline]
fn from(list: &MessageOptionsList) -> Self {
let mut encoded = Vec::with_capacity(MIN_OPTIONS_SIZE);
list.0
.iter()
.for_each(|option| option.extend_into(&mut encoded));
encoded
}
}