Add DHCP Option parsing for RFC1497 Options
This commit is contained in:
parent
0bf69237b9
commit
43a0c5926c
@ -8,3 +8,6 @@ edition = "2021"
|
||||
[dependencies.nom]
|
||||
version = "7"
|
||||
default-features = false
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1"
|
61
src/lib.rs
61
src/lib.rs
@ -1,14 +1,19 @@
|
||||
#![allow(unused)]
|
||||
use std::{net::Ipv4Addr, path::Path};
|
||||
//#![no_std]
|
||||
use alloc::vec::Vec;
|
||||
use core::net::Ipv4Addr;
|
||||
use options::{DhcpOption, RawDhcpOption, DhcpOptionError};
|
||||
|
||||
use nom::{
|
||||
bytes::complete::{take, take_until},
|
||||
bytes::complete::{take, take_until, take_while},
|
||||
multi::many0_count,
|
||||
number::complete::{be_u16, be_u32, be_u8},
|
||||
IResult,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DhcpMessage<'a> {
|
||||
struct Dhcpv4Message<'a> {
|
||||
operation: Operation,
|
||||
hardware_type: HardwareType,
|
||||
hardware_address_length: u8,
|
||||
@ -22,6 +27,10 @@ struct DhcpMessage<'a> {
|
||||
client_hardware_address: &'a [u8],
|
||||
server_name: &'a str,
|
||||
file_name: &'a Path,
|
||||
{
|
||||
let opt = self.options.iter().find(|a| a.code == T::CODE)?;
|
||||
Some(T::parse(opt))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -61,11 +70,10 @@ impl From<u8> for HardwareType {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_dhcp_operation(i: &[u8]) -> IResult<&[u8], Operation> {
|
||||
be_u8(i).map(|(i, v)| (i, v.into()))
|
||||
}
|
||||
|
||||
fn parse_dhcp_flags(i: &[u8]) -> IResult<&[u8], Flags> {
|
||||
fn parse_flags(i: &[u8]) -> IResult<&[u8], Flags> {
|
||||
be_u16(i).map(|(i, v)| (i, Flags(v)))
|
||||
}
|
||||
|
||||
@ -86,8 +94,9 @@ fn parse_client_hardware_address(i: &[u8], hardware_address_length: u8) -> IResu
|
||||
}
|
||||
|
||||
fn parse_server_name(i: &[u8]) -> IResult<&[u8], &str> {
|
||||
let nullbyte_position = i.iter().position(|v| *v == b'\0').unwrap_or(64);
|
||||
let val = unsafe { std::str::from_utf8_unchecked(&i[..nullbyte_position]) };
|
||||
let (i, val) = take(64usize)(i)?;
|
||||
let nullbyte_position = val.iter().position(|v| *v == b'\0').unwrap_or(64);
|
||||
let val = unsafe { core::str::from_utf8_unchecked(&val[..nullbyte_position]) };
|
||||
|
||||
Ok((i, val))
|
||||
}
|
||||
@ -95,18 +104,28 @@ fn parse_server_name(i: &[u8]) -> IResult<&[u8], &str> {
|
||||
fn parse_file(i: &[u8]) -> IResult<&[u8], &Path> {
|
||||
let nullbyte_position = i.iter().position(|v| *v == b'\0').unwrap_or(128);
|
||||
let val = unsafe { std::str::from_utf8_unchecked(&i[..nullbyte_position]) };
|
||||
fn parse_option(i: &[u8]) -> IResult<&[u8], RawDhcpOption> {
|
||||
let (i, code) = be_u8(i)?;
|
||||
let (i, length) = be_u8(i)?;
|
||||
let (i, data) = take(length)(i)?;
|
||||
|
||||
Ok((i, RawDhcpOption { code, length, data }))
|
||||
}
|
||||
|
||||
fn parse_file(i: &[u8]) -> IResult<&[u8], &str> {
|
||||
let (i, val) = take(128usize)(i)?;
|
||||
|
||||
Ok((i, Path::new(val)))
|
||||
}
|
||||
|
||||
fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], DhcpMessage> {
|
||||
let (packet, operation) = parse_dhcp_operation(packet)?;
|
||||
fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], Dhcpv4Message> {
|
||||
let (packet, operation) = parse_operation(packet)?;
|
||||
let (packet, hardware_type) = parse_dhcp_hardware_type(packet)?;
|
||||
let (packet, hardware_address_length) = be_u8(packet)?;
|
||||
let (packet, hops) = be_u8(packet)?;
|
||||
let (packet, transaction_id) = be_u32(packet)?;
|
||||
let (packet, seconds) = be_u16(packet)?;
|
||||
let (packet, flags) = parse_dhcp_flags(packet)?;
|
||||
let (packet, flags) = parse_flags(packet)?;
|
||||
let (packet, client_address) = parse_ipv4_address(packet)?;
|
||||
let (packet, server_address) = parse_ipv4_address(packet)?;
|
||||
let (packet, gateway_address) = parse_ipv4_address(packet)?;
|
||||
@ -117,10 +136,21 @@ fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], DhcpMessage> {
|
||||
|
||||
let (_, server_name) = parse_server_name(server_name_bytes)?;
|
||||
let (_, file_name) = parse_file(file_name_bytes)?;
|
||||
if let Ok((mut packet, magic)) = take::<_, _, ()>(4usize)(packet) {
|
||||
while !packet.is_empty() && packet[0] != 0xff {
|
||||
let (remaining, option) = parse_option(packet)?;
|
||||
options.push(option);
|
||||
let (remaining, _) = take_while(|x| x == 0x00)(remaining)?;
|
||||
packet = remaining;
|
||||
}
|
||||
let (_, _server_name) = parse_server_name(server_name_bytes)?;
|
||||
server_name = Some(_server_name);
|
||||
let (_, _file_name) = parse_file(file_name_bytes)?;
|
||||
}
|
||||
|
||||
Ok((
|
||||
packet,
|
||||
DhcpMessage {
|
||||
Dhcpv4Message {
|
||||
operation,
|
||||
hardware_type,
|
||||
hardware_address_length,
|
||||
@ -168,11 +198,18 @@ mod tests {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x63, 0x82, 0x53, 0x63, // Magic cookie
|
||||
0x01, 0x04, 0x7f, 0x00, 0x00, 0x01, // Subnet Mask Option
|
||||
0x00, 0x00, 0x00, // Paddings
|
||||
0x03, 0x08, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x02// Router Option
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn parses() {
|
||||
let res = super::parse_dhcp_packet(DHCP_PACKET).unwrap();
|
||||
dbg!(res);
|
||||
use crate::options;
|
||||
let (buf, res) = super::parse_dhcp_packet(DHCP_PACKET).unwrap();
|
||||
dbg!(&res);
|
||||
dbg!(res.get_option::<options::SubnetMask>());
|
||||
dbg!(res.get_option::<options::RouterOption>());
|
||||
}
|
||||
}
|
||||
|
17
src/options/boot_file_size_option.rs
Normal file
17
src/options/boot_file_size_option.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use super::{DhcpOptionError, RawDhcpOption};
|
||||
use crate::options::DhcpOption;
|
||||
|
||||
pub struct BootFileSizeOption;
|
||||
|
||||
impl DhcpOption for BootFileSizeOption {
|
||||
const CODE: u8 = 13;
|
||||
type Item = u16;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
(option.length == 2)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, 2))?;
|
||||
let data = option.data[..2].try_into().expect("Parser pulled 2 bytes");
|
||||
Ok(u16::from_be_bytes(data))
|
||||
}
|
||||
}
|
22
src/options/cookie_server_option.rs
Normal file
22
src/options/cookie_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{DhcpOptionError, RawDhcpOption};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct CookieServerOption;
|
||||
|
||||
impl DhcpOption for CookieServerOption {
|
||||
const CODE: u8 = 8;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
15
src/options/domain_name.rs
Normal file
15
src/options/domain_name.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct DomainName;
|
||||
|
||||
impl DhcpOption for DomainName {
|
||||
const CODE: u8 = 15;
|
||||
type Item = String;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let data = core::str::from_utf8(&option.data)?;
|
||||
Ok(data.to_string())
|
||||
}
|
||||
}
|
22
src/options/domain_name_server_option.rs
Normal file
22
src/options/domain_name_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{DhcpOptionError, RawDhcpOption};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct DomainNameServerOption;
|
||||
|
||||
impl DhcpOption for DomainNameServerOption {
|
||||
const CODE: u8 = 6;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
15
src/options/extensions_path.rs
Normal file
15
src/options/extensions_path.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct ExtensionsPath;
|
||||
|
||||
impl DhcpOption for ExtensionsPath {
|
||||
const CODE: u8 = 18;
|
||||
type Item = String;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let data = core::str::from_utf8(&option.data)?;
|
||||
Ok(data.to_string())
|
||||
}
|
||||
}
|
15
src/options/host_name_option.rs
Normal file
15
src/options/host_name_option.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct HostNameOption;
|
||||
|
||||
impl DhcpOption for HostNameOption {
|
||||
const CODE: u8 = 12;
|
||||
type Item = String;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let data = core::str::from_utf8(&option.data)?;
|
||||
Ok(data.to_string())
|
||||
}
|
||||
}
|
22
src/options/impress_server_option.rs
Normal file
22
src/options/impress_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct ImpressServerOption;
|
||||
|
||||
impl DhcpOption for ImpressServerOption {
|
||||
const CODE: u8 = 10;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
22
src/options/log_server_option.rs
Normal file
22
src/options/log_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct LogServerOption;
|
||||
|
||||
impl DhcpOption for LogServerOption {
|
||||
const CODE: u8 = 7;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
22
src/options/lpr_server_option.rs
Normal file
22
src/options/lpr_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct LPRServerOption;
|
||||
|
||||
impl DhcpOption for LPRServerOption {
|
||||
const CODE: u8 = 9;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
15
src/options/merit_dump_file.rs
Normal file
15
src/options/merit_dump_file.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct MeritDumpFile;
|
||||
|
||||
impl DhcpOption for MeritDumpFile {
|
||||
const CODE: u8 = 14;
|
||||
type Item = String;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let data = core::str::from_utf8(&option.data)?;
|
||||
Ok(data.to_string())
|
||||
}
|
||||
}
|
74
src/options/mod.rs
Normal file
74
src/options/mod.rs
Normal file
@ -0,0 +1,74 @@
|
||||
mod boot_file_size_option;
|
||||
mod cookie_server_option;
|
||||
mod domain_name_server_option;
|
||||
mod host_name_option;
|
||||
mod impress_server_option;
|
||||
mod log_server_option;
|
||||
mod lpr_server_option;
|
||||
mod name_server_option;
|
||||
mod resource_location_server_option;
|
||||
mod router_option;
|
||||
mod subnet_mask;
|
||||
mod time_offset;
|
||||
mod time_server_option;
|
||||
mod merit_dump_file;
|
||||
mod domain_name;
|
||||
mod swap_server;
|
||||
mod root_path;
|
||||
mod extensions_path;
|
||||
|
||||
pub use boot_file_size_option::BootFileSizeOption;
|
||||
pub use cookie_server_option::CookieServerOption;
|
||||
pub use domain_name_server_option::DomainNameServerOption;
|
||||
pub use impress_server_option::ImpressServerOption;
|
||||
pub use log_server_option::LogServerOption;
|
||||
pub use lpr_server_option::LPRServerOption;
|
||||
pub use name_server_option::NameServerOption;
|
||||
pub use resource_location_server_option::ResourceLocationServerOption;
|
||||
pub use router_option::RouterOption;
|
||||
pub use subnet_mask::SubnetMask;
|
||||
pub use time_offset::TimeOffset;
|
||||
pub use merit_dump_file::MeritDumpFile;
|
||||
pub use domain_name::DomainName;
|
||||
pub use swap_server::SwapServer;
|
||||
pub use root_path::RootPath;
|
||||
pub use extensions_path::ExtensionsPath;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RawDhcpOption<'a> {
|
||||
pub code: u8,
|
||||
pub length: u8,
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
pub trait DhcpOption {
|
||||
const CODE: u8;
|
||||
type Item;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DhcpOptionError {
|
||||
InvalidLength(u8, u8),
|
||||
UTF8Error,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for DhcpOptionError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DhcpOptionError::InvalidLength(got, should) => {
|
||||
write!(f, "Invalid length got {got}, should be {should}")
|
||||
}
|
||||
DhcpOptionError::UTF8Error => write!(f, "Error parsing option data as UTF-8"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::error::Error for DhcpOptionError {}
|
||||
|
||||
impl From<core::str::Utf8Error> for DhcpOptionError {
|
||||
fn from(value: core::str::Utf8Error) -> Self {
|
||||
DhcpOptionError::UTF8Error
|
||||
}
|
||||
}
|
22
src/options/name_server_option.rs
Normal file
22
src/options/name_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct NameServerOption;
|
||||
|
||||
impl DhcpOption for NameServerOption {
|
||||
const CODE: u8 = 5;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
22
src/options/resource_location_server_option.rs
Normal file
22
src/options/resource_location_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct ResourceLocationServerOption;
|
||||
|
||||
impl DhcpOption for ResourceLocationServerOption {
|
||||
const CODE: u8 = 11;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
15
src/options/root_path.rs
Normal file
15
src/options/root_path.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct RootPath;
|
||||
|
||||
impl DhcpOption for RootPath {
|
||||
const CODE: u8 = 17;
|
||||
type Item = String;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let data = core::str::from_utf8(&option.data)?;
|
||||
Ok(data.to_string())
|
||||
}
|
||||
}
|
22
src/options/router_option.rs
Normal file
22
src/options/router_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct RouterOption;
|
||||
|
||||
impl DhcpOption for RouterOption {
|
||||
const CODE: u8 = 3;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
22
src/options/subnet_mask.rs
Normal file
22
src/options/subnet_mask.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{DhcpOptionError, RawDhcpOption};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct SubnetMask;
|
||||
|
||||
impl DhcpOption for SubnetMask {
|
||||
const CODE: u8 = 1;
|
||||
type Item = Ipv4Addr;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
(option.length == 4)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, 4))?;
|
||||
Ok(Ipv4Addr::new(
|
||||
option.data[0],
|
||||
option.data[1],
|
||||
option.data[2],
|
||||
option.data[3],
|
||||
))
|
||||
}
|
||||
}
|
22
src/options/swap_server.rs
Normal file
22
src/options/swap_server.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{DhcpOptionError, RawDhcpOption};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct SwapServer;
|
||||
|
||||
impl DhcpOption for SwapServer {
|
||||
const CODE: u8 = 16;
|
||||
type Item = Ipv4Addr;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
(option.length == 4)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, 4))?;
|
||||
Ok(Ipv4Addr::new(
|
||||
option.data[0],
|
||||
option.data[1],
|
||||
option.data[2],
|
||||
option.data[3],
|
||||
))
|
||||
}
|
||||
}
|
20
src/options/time_offset.rs
Normal file
20
src/options/time_offset.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
|
||||
pub struct TimeOffset;
|
||||
|
||||
impl DhcpOption for TimeOffset {
|
||||
const CODE: u8 = 2;
|
||||
type Item = u32;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
(option.length == 4)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, 4))?;
|
||||
Ok(u32::from_be_bytes(
|
||||
option.data[..4]
|
||||
.try_into()
|
||||
.expect("The parser pulled exactly 4 bytes"),
|
||||
))
|
||||
}
|
||||
}
|
22
src/options/time_server_option.rs
Normal file
22
src/options/time_server_option.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::{RawDhcpOption, DhcpOptionError};
|
||||
use crate::options::DhcpOption;
|
||||
use core::net::Ipv4Addr;
|
||||
|
||||
pub struct TimeServerOption;
|
||||
|
||||
impl DhcpOption for TimeServerOption {
|
||||
const CODE: u8 = 4;
|
||||
type Item = Vec<Ipv4Addr>;
|
||||
|
||||
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
|
||||
let rem = option.length % 4;
|
||||
(rem == 0)
|
||||
.then_some(())
|
||||
.ok_or(DhcpOptionError::InvalidLength(option.length, option.length + 4 - rem))?;
|
||||
Ok(option
|
||||
.data
|
||||
.chunks(4)
|
||||
.map(|c| Ipv4Addr::new(c[0], c[1], c[2], c[3]))
|
||||
.collect())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user