216 lines
7.0 KiB
Rust
216 lines
7.0 KiB
Rust
#![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, take_while},
|
|
multi::many0_count,
|
|
number::complete::{be_u16, be_u32, be_u8},
|
|
IResult,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
struct Dhcpv4Message<'a> {
|
|
operation: Operation,
|
|
hardware_type: HardwareType,
|
|
hardware_address_length: u8,
|
|
hops: u8,
|
|
transaction_id: u32,
|
|
seconds: u16,
|
|
flags: Flags,
|
|
client_address: Ipv4Addr,
|
|
server_address: Ipv4Addr,
|
|
gateway_address: Ipv4Addr,
|
|
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)]
|
|
struct Flags(u16);
|
|
|
|
#[derive(Debug)]
|
|
enum Operation {
|
|
Request = 1,
|
|
Reply = 2,
|
|
}
|
|
|
|
impl From<u8> for Operation {
|
|
fn from(value: u8) -> Self {
|
|
match value {
|
|
1 => Operation::Request,
|
|
2 => Operation::Reply,
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum HardwareType {
|
|
Reserved(u8),
|
|
Unassigned(u8),
|
|
Ethernet,
|
|
}
|
|
|
|
impl From<u8> for HardwareType {
|
|
fn from(value: u8) -> Self {
|
|
use HardwareType::*;
|
|
match value {
|
|
a @ 0 => Reserved(a),
|
|
1 => Ethernet,
|
|
v => Unassigned(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
be_u8(i).map(|(i, v)| (i, v.into()))
|
|
}
|
|
|
|
fn parse_flags(i: &[u8]) -> IResult<&[u8], Flags> {
|
|
be_u16(i).map(|(i, v)| (i, Flags(v)))
|
|
}
|
|
|
|
fn parse_dhcp_hardware_type(i: &[u8]) -> IResult<&[u8], HardwareType> {
|
|
be_u8(i).map(|(i, v)| (i, v.into()))
|
|
}
|
|
|
|
fn parse_ipv4_address(i: &[u8]) -> IResult<&[u8], Ipv4Addr> {
|
|
let (i, val) = take(4usize)(i)?;
|
|
let ip = Ipv4Addr::new(val[0], val[1], val[2], val[3]);
|
|
Ok((i, ip))
|
|
}
|
|
|
|
fn parse_client_hardware_address(i: &[u8], hardware_address_length: u8) -> IResult<&[u8], &[u8]> {
|
|
let (i, val) = take(hardware_address_length)(i)?;
|
|
let (i, _padding) = take(0x10 - hardware_address_length)(i)?;
|
|
Ok((i, val))
|
|
}
|
|
|
|
fn parse_server_name(i: &[u8]) -> IResult<&[u8], &str> {
|
|
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))
|
|
}
|
|
|
|
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], 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_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)?;
|
|
let (packet, client_hardware_address) =
|
|
parse_client_hardware_address(packet, hardware_address_length)?;
|
|
let (packet, server_name_bytes) = take(64usize)(packet)?;
|
|
let (packet, file_name_bytes) = take(128usize)(packet)?;
|
|
|
|
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,
|
|
Dhcpv4Message {
|
|
operation,
|
|
hardware_type,
|
|
hardware_address_length,
|
|
hops,
|
|
transaction_id,
|
|
seconds,
|
|
flags,
|
|
client_address,
|
|
server_address,
|
|
gateway_address,
|
|
client_hardware_address,
|
|
server_name,
|
|
file_name,
|
|
},
|
|
))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[rustfmt::skip]
|
|
static DHCP_PACKET: &'static [u8] = &[
|
|
0x01, // op
|
|
0x01, // htype
|
|
0x06, // hlen
|
|
0x05, // hops
|
|
0x00, 0x00, 0x00, 0x01, // xid
|
|
0x00, 0x02, // secs
|
|
0x00, 0x01, // flags
|
|
0x7f, 0x00, 0x00, 0x01, // ciaddr
|
|
0x7f, 0x00, 0x00, 0x01, // siaddr
|
|
0x7f, 0x00, 0x00, 0x01, // giaddr
|
|
// chaddr
|
|
0x54, 0x05, 0xdb, 0xa6, 0x96, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// server name
|
|
0x49, 0x20, 0x62, 0x69, 0x6D, 0x73, 0x20, 0x31, 0x20, 0x44, 0x48, 0x43, 0x50, 0x20, 0x53, 0x65,
|
|
0x72, 0x76, 0x65, 0x72, 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,
|
|
// file
|
|
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,
|
|
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,
|
|
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() {
|
|
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>());
|
|
}
|
|
}
|