dhcp/src/lib.rs

179 lines
5.6 KiB
Rust

#![allow(unused)]
use std::{net::Ipv4Addr, path::Path};
use nom::{
bytes::complete::{take, take_until},
number::complete::{be_u16, be_u32, be_u8},
IResult,
};
#[derive(Debug)]
struct DhcpMessage<'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,
}
#[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),
}
}
}
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> {
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 nullbyte_position = i.iter().position(|v| *v == b'\0').unwrap_or(64);
let val = unsafe { std::str::from_utf8_unchecked(&i[..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]) };
Ok((i, Path::new(val)))
}
fn parse_dhcp_packet(packet: &[u8]) -> IResult<&[u8], DhcpMessage> {
let (packet, operation) = parse_dhcp_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, 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)?;
Ok((
packet,
DhcpMessage {
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,
];
#[test]
fn parses() {
let res = super::parse_dhcp_packet(DHCP_PACKET).unwrap();
dbg!(res);
}
}