Rework structure

Remove the "file per option" structure and replace it with a macro that generates the `DhcpOption` impl.

This also generates a `Code` enum with all the options are variants. This will later be used for parameter requests and programmatic checking if certain options exist.

Also fixes a bug where I have left out the `your_address` field.
This commit is contained in:
fuckwit 2023-06-04 22:52:14 +02:00
parent 2639aeca24
commit b095fa96de
21 changed files with 256 additions and 394 deletions

View File

@ -1,7 +1,7 @@
#![allow(unused)]
#![feature(ip_in_core)]
#![feature(error_in_core)]
mod options;
pub mod options;
extern crate alloc;
//#![no_std]
@ -17,7 +17,7 @@ use nom::{
};
#[derive(Debug)]
struct Dhcpv4Message<'a> {
pub struct Dhcpv4Message<'a> {
operation: Operation,
hardware_type: HardwareType,
hardware_address_length: u8,
@ -26,6 +26,7 @@ struct Dhcpv4Message<'a> {
seconds: u16,
flags: Flags,
client_address: Ipv4Addr,
your_address: Ipv4Addr,
server_address: Ipv4Addr,
gateway_address: Ipv4Addr,
client_hardware_address: &'a [u8],
@ -35,7 +36,7 @@ struct Dhcpv4Message<'a> {
}
impl<'a> Dhcpv4Message<'_> {
fn get_option<T>(&self) -> Option<Result<T::Item, DhcpOptionError>>
pub fn get_option<T>(&self) -> Option<Result<T::Item, DhcpOptionError>>
where
T: DhcpOption,
{
@ -129,7 +130,7 @@ fn parse_file(i: &[u8]) -> IResult<&[u8], &str> {
Ok((i, val))
}
fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], Dhcpv4Message> {
pub 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)?;
@ -138,6 +139,7 @@ fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], Dhcpv4Message> {
let (packet, seconds) = be_u16(packet)?;
let (packet, flags) = parse_flags(packet)?;
let (packet, client_address) = parse_ipv4_address(packet)?;
let (packet, your_address) = parse_ipv4_address(packet)?;
let (packet, server_address) = parse_ipv4_address(packet)?;
let (packet, gateway_address) = parse_ipv4_address(packet)?;
let (packet, client_hardware_address) =
@ -148,7 +150,9 @@ fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], Dhcpv4Message> {
let mut options = Vec::new();
let mut server_name = None;
let mut file_name = None;
if let Ok((mut packet, magic)) = take::<_, _, ()>(4usize)(packet) {
let mut packet = packet;
if let Ok((packet2, magic)) = take::<_, _, ()>(4usize)(packet) {
while !packet.is_empty() && packet[0] != 0xff {
let (remaining, option) = parse_option(packet)?;
options.push(option);
@ -171,6 +175,7 @@ fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], Dhcpv4Message> {
seconds,
flags,
client_address,
your_address,
server_address,
gateway_address,
client_hardware_address,
@ -193,6 +198,7 @@ mod tests {
0x00, 0x02, // secs
0x00, 0x01, // flags
0x7f, 0x00, 0x00, 0x01, // ciaddr
0x7f, 0x00, 0x00, 0x01, // yiaddr
0x7f, 0x00, 0x00, 0x01, // siaddr
0x7f, 0x00, 0x00, 0x01, // giaddr
// chaddr
@ -216,6 +222,20 @@ mod tests {
0x00, 0x00, 0x00, // Paddings
0x03, 0x08, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x02// Router Option
];
static PACKET: &'static [u8] = &[
1, // op
1, // htype
6, // htype len
0, // hops
218, 125, 111, 190, // xid
0, 0, //seconds
0, 0, //flags
0, 0, 0, 0, // ciaddr
0, 0, 0, 0, // yiaddr
0, 0, 0, 0, // siaddr
0, 0, 0, 0, // giaddr
232, 219, 132, 155, 251, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 130, 83, 99, 53, 1, 3, 57, 2, 5, 220, 50, 4, 192, 168, 1, 106, 54, 4, 192, 168, 1, 1, 55, 5, 1, 3, 28, 6, 42, 12, 10, 69, 83, 80, 45, 57, 66, 70, 66, 55, 66, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
#[test]
fn parses() {

19
src/main.rs Normal file
View File

@ -0,0 +1,19 @@
use dhcp::parse_dhcp_packet;
use std::net::UdpSocket;
fn main() {
let sock = UdpSocket::bind("0.0.0.0:67").unwrap();
let mut buf = [0; 504];
while let Ok((size, addr)) = sock.recv_from(&mut buf) {
println!("Got {size} bytes from {addr}");
//println!("{buf:?}");
let (_, msg) = parse_dhcp_packet(&buf).unwrap();
dbg!(&msg);
let hostname = msg
.get_option::<dhcp::options::HostNameOption>()
.unwrap()
.unwrap();
println!("Hostname: {hostname}");
}
}

View File

@ -1,17 +0,0 @@
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))
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,15 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,15 +0,0 @@
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())
}
}

View File

@ -1,15 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,15 +0,0 @@
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())
}
}

View File

@ -1,38 +1,215 @@
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;
use core::net::Ipv4Addr;
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;
macro_rules! define_dhcp_options {
($($code:expr, $name:ident, $data:ty),+) => {
enum Code {
$($name = $code),+
}
$(
pub struct $name(pub $data);
impl DhcpOption for $name {
const CODE: u8 = $code;
type Item = $data;
fn parse(option: &RawDhcpOption) -> Result<Self::Item, DhcpOptionError> {
TryFrom::try_from(option)
}
}
)+
};
}
#[rustfmt::skip]
define_dhcp_options!(
1, SubnetMask, Ipv4Addr,
2, TimeOffset, u32,
3, RouterOption, Vec<Ipv4Addr>,
4, TimeServerOption, Vec<Ipv4Addr>,
5, NameServerOption, Vec<Ipv4Addr>,
6, DomainNameServerOption, Vec<Ipv4Addr>,
7, LogServerOption, Vec<Ipv4Addr>,
8, CookieServerOption, Vec<Ipv4Addr>,
9, LprServerOption, Vec<Ipv4Addr>,
10, ImpressServerOption, Vec<Ipv4Addr>,
11, ResourceLocationServerOption, Vec<Ipv4Addr>,
12, HostNameOption, String,
13, BootFileSizeOption, u16,
14, MeritDumpFile, String,
15, DomainName, String,
16, SwapServer, Vec<Ipv4Addr>,
17, RootPath, String,
18, ExtensionPath, String,
19, IPForwardingOption, bool,
20, NonLocalSourceRoutingOption, bool,
21, PolicyFilterOption, Vec<(Ipv4Addr, Ipv4Addr)>,
22, MaximumDatagramReassemblySize, u16,
23, DefaultIPTimeToLive, u8,
24, PathMTUAgingTimeoutOption, u32,
25, PathMTUPlateauTableOption, Vec<u16>,
26, InterfaceMTUOption, u16,
27, AllSubnetsAreLocalOption, bool,
28, BroadcastAddressOption, Ipv4Addr,
29, PerformMaskDiscoveryOption, bool,
30, MaskSupplierOption, bool,
31, PerformRouterDiscoveryOption, bool,
32, RouterSolicitationAddressOption, Ipv4Addr,
33, StaticRouteOption, Vec<(Ipv4Addr, Ipv4Addr)>,
34, TrailerEncapsulationOption, bool,
35, ARPCacheTimeoutOption, u32,
36, EthernetEncapsulationOption, bool,
37, TCPDefaultTTLOption, u8,
38, TCPKeepaliveIntervalOption, u32,
39, TCPKeepaliveGarbageOption, bool,
40, NetworkInformationServiceDomainOption, String,
41, NetworkInformationServersOption, Vec<Ipv4Addr>,
42, NetworkTimeProtocolServersOption, Vec<Ipv4Addr>,
43, VendorSpecificInformation, Vec<u8>,
44, NetBIOSOverTCPIPNameServerOption, Vec<Ipv4Addr>,
45, NetBIOSOverTCPIPDatagramDistributionServerOption, Vec<Ipv4Addr>,
46, NetBIOSOverTCPIPNodeTypeOption, u8, //TODO: Make separate type. RFC2132 section 8.6
47, NetBIOSOverTCPIPScopeOption, String,
48, XWindowSystemFontServerOption, Vec<Ipv4Addr>
);
impl<'a> TryFrom<&RawDhcpOption<'a>> for Ipv4Addr {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
(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],
))
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for u16 {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
(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))
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for Vec<u16> {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
let rem = option.length % 2;
(rem == 0)
.then_some(())
.ok_or(DhcpOptionError::InvalidLength(
option.length,
option.length + 2 - rem,
))?;
Ok(option
.data
.chunks(2)
.map(|c| u16::from_be_bytes(c.try_into().expect("validated before")))
.collect())
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for u8 {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
(option.length == 1)
.then_some(())
.ok_or(DhcpOptionError::InvalidLength(option.length, 1))?;
Ok(option.data[0])
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for u32 {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
(option.length == 4)
.then_some(())
.ok_or(DhcpOptionError::InvalidLength(option.length, 2))?;
let data = option.data[..4].try_into().expect("Parser pulled 4 bytes");
Ok(u32::from_be_bytes(data))
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for Vec<Ipv4Addr> {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
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())
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for Vec<u8> {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
Ok(option.data.to_vec())
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for String {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
let data = core::str::from_utf8(option.data)?;
Ok(data.to_string())
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for bool {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
Ok(option.data[0] == 1)
}
}
impl<'a> TryFrom<&RawDhcpOption<'a>> for Vec<(Ipv4Addr, Ipv4Addr)> {
type Error = DhcpOptionError;
fn try_from(option: &RawDhcpOption<'a>) -> Result<Self, Self::Error> {
let rem = option.length % 8;
(rem == 0)
.then_some(())
.ok_or(DhcpOptionError::InvalidLength(
option.length,
option.length + 8 - rem,
))?;
Ok(option
.data
.chunks(8)
.map(|c| {
(
Ipv4Addr::new(c[0], c[1], c[2], c[3]),
Ipv4Addr::new(c[4], c[5], c[6], c[7]),
)
})
.collect())
}
}
#[derive(Debug)]
pub struct RawDhcpOption<'a> {
@ -71,4 +248,4 @@ impl From<core::str::Utf8Error> for DhcpOptionError {
fn from(value: core::str::Utf8Error) -> Self {
DhcpOptionError::UTF8Error
}
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,15 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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())
}
}

View File

@ -1,22 +0,0 @@
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],
))
}
}

View File

@ -1,22 +0,0 @@
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],
))
}
}

View File

@ -1,20 +0,0 @@
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"),
))
}
}

View File

@ -1,22 +0,0 @@
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())
}
}