From e8d62af6c35b3327ca2a2cc80bd7374dea0dd4e2 Mon Sep 17 00:00:00 2001 From: junderw Date: Mon, 30 Oct 2023 20:13:21 -0700 Subject: [PATCH 1/3] Minor fixes --- .gitignore | 2 ++ examples/monitor.rs | 3 ++- examples/server.rs | 5 +---- src/lib.rs | 26 -------------------------- 4 files changed, 5 insertions(+), 31 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/examples/monitor.rs b/examples/monitor.rs index 36a4915..74bb21f 100644 --- a/examples/monitor.rs +++ b/examples/monitor.rs @@ -8,6 +8,7 @@ fn main() { server::Server::serve( UdpSocket::bind("0.0.0.0:67").unwrap(), Ipv4Addr::new(0, 0, 0, 0), + Ipv4Addr::new(0, 0, 0, 0), MyServer {}, ); } @@ -24,7 +25,7 @@ impl server::Handler for MyServer { }; println!( "{}\t{}\t{}\tOnline", - time::OffsetDateTime::now_local().format("%Y-%m-%dT%H:%M:%S"), + time::OffsetDateTime::try_now_local().unwrap().format("%Y-%m-%dT%H:%M:%S"), chaddr(&in_packet.chaddr), Ipv4Addr::from(req_ip) ); diff --git a/examples/server.rs b/examples/server.rs index f09cd41..60f33e3 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,6 +1,3 @@ -#[macro_use(u32_bytes, bytes_u32)] -extern crate dhcp4r; - use std::collections::HashMap; use std::net::{Ipv4Addr, UdpSocket}; use std::ops::Add; @@ -24,7 +21,7 @@ const LEASE_DURATION_SECS: u32 = 86400; const LEASE_NUM: u32 = 252; // Derived constants -const IP_START_NUM: u32 = bytes_u32!(IP_START); +const IP_START_NUM: u32 = u32::from_be_bytes(IP_START); const INFINITE_LEASE: Option = None; // Special value for infinite lease diff --git a/src/lib.rs b/src/lib.rs index f13167b..e239a0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,33 +1,7 @@ - - pub mod options; pub mod packet; pub mod server; -/// Converts a u32 to 4 bytes (Big endian) -#[macro_export] -macro_rules! u32_bytes { - ( $x:expr ) => { - [ - ($x >> 24) as u8, - ($x >> 16) as u8, - ($x >> 8) as u8, - $x as u8, - ] - }; -} - -/// Converts 4 bytes to a u32 (Big endian) -#[macro_export] -macro_rules! bytes_u32 { - ( $x:expr ) => { - ($x[0] as u32) * (1 << 24) - + ($x[1] as u32) * (1 << 16) - + ($x[2] as u32) * (1 << 8) - + ($x[3] as u32) - }; -} - #[cfg(test)] mod tests { #[test] From 4a85f1dcbb9a444a753ce7cdb194065485f49410 Mon Sep 17 00:00:00 2001 From: junderw Date: Mon, 30 Oct 2023 20:13:40 -0700 Subject: [PATCH 2/3] Run cargo fmt --- examples/monitor.rs | 4 +- examples/server.rs | 83 +++++++++++++++++----------------- src/options.rs | 2 +- src/packet.rs | 107 +++++++++++++++++++++----------------------- src/server.rs | 29 ++++++------ 5 files changed, 112 insertions(+), 113 deletions(-) diff --git a/examples/monitor.rs b/examples/monitor.rs index 74bb21f..1a07212 100644 --- a/examples/monitor.rs +++ b/examples/monitor.rs @@ -25,7 +25,9 @@ impl server::Handler for MyServer { }; println!( "{}\t{}\t{}\tOnline", - time::OffsetDateTime::try_now_local().unwrap().format("%Y-%m-%dT%H:%M:%S"), + time::OffsetDateTime::try_now_local() + .unwrap() + .format("%Y-%m-%dT%H:%M:%S"), chaddr(&in_packet.chaddr), Ipv4Addr::from(req_ip) ); diff --git a/examples/server.rs b/examples/server.rs index 60f33e3..ea70b40 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; +use std::fs::File; +use std::io::{BufRead, BufReader}; use std::net::{Ipv4Addr, UdpSocket}; use std::ops::Add; use std::time::{Duration, Instant}; -use std::fs::File; -use std::io::{BufRead, BufReader}; use dhcp4r::{options, packet, server}; @@ -24,39 +24,38 @@ const LEASE_NUM: u32 = 252; const IP_START_NUM: u32 = u32::from_be_bytes(IP_START); const INFINITE_LEASE: Option = None; // Special value for infinite lease - fn main() { let socket = UdpSocket::bind("0.0.0.0:67").unwrap(); socket.set_broadcast(true).unwrap(); - let mut leases: HashMap)> = HashMap::new(); + let mut leases: HashMap)> = HashMap::new(); // Read and populate leases from the file -if let Ok(file) = File::open("leases") { - let reader = BufReader::new(file); - for line in reader.lines() { - if let Ok(line) = line { - let parts: Vec<&str> = line.split(',').collect(); - if parts.len() == 2 { - let mac_parts: Vec = parts[0] - .split(':') - .filter_map(|part| u8::from_str_radix(part, 16).ok()) - .collect(); + if let Ok(file) = File::open("leases") { + let reader = BufReader::new(file); + for line in reader.lines() { + if let Ok(line) = line { + let parts: Vec<&str> = line.split(',').collect(); + if parts.len() == 2 { + let mac_parts: Vec = parts[0] + .split(':') + .filter_map(|part| u8::from_str_radix(part, 16).ok()) + .collect(); - if mac_parts.len() == 6 { - let mut mac = [0u8; 6]; - mac.copy_from_slice(&mac_parts); + if mac_parts.len() == 6 { + let mut mac = [0u8; 6]; + mac.copy_from_slice(&mac_parts); - let ip = parts[1].trim().parse::().unwrap(); - leases.insert(ip, (mac, INFINITE_LEASE)); + let ip = parts[1].trim().parse::().unwrap(); + leases.insert(ip, (mac, INFINITE_LEASE)); + } } } } + } else { + eprintln!("Failed to open leases file. Continuing..."); + //return; } -} else { - eprintln!("Failed to open leases file. Continuing..."); - //return; -} - + let ms = MyServer { leases, last_lease: 0, @@ -76,10 +75,9 @@ impl server::Handler for MyServer { fn handle_request(&mut self, server: &server::Server, in_packet: packet::Packet) { match in_packet.message_type() { Ok(options::MessageType::Discover) => { - // Otherwise prefer existing (including expired if available) if let Some(ip) = self.current_lease(&in_packet.chaddr) { - println!("Sending Reply to discover"); + println!("Sending Reply to discover"); reply(server, options::MessageType::Offer, in_packet, &ip); return; } @@ -90,7 +88,7 @@ impl server::Handler for MyServer { &in_packet.chaddr, &((IP_START_NUM + &self.last_lease).into()), ) { - println!("Sending Reply to discover"); + println!("Sending Reply to discover"); reply( server, options::MessageType::Offer, @@ -105,29 +103,35 @@ impl server::Handler for MyServer { Ok(options::MessageType::Request) => { // Ignore requests to alternative DHCP server if !server.for_this_server(&in_packet) { - //println!("Not for this server"); - // return; + //println!("Not for this server"); + // return; } - + let req_ip = match in_packet.option(options::REQUESTED_IP_ADDRESS) { Some(options::DhcpOption::RequestedIpAddress(x)) => *x, _ => in_packet.ciaddr, }; - for (ip, (mac, _)) in &self.leases { - println!("IP: {:?}, MAC: {:?}", ip, mac); - } - if let Some(ip) = self.current_lease(&in_packet.chaddr) { - println!("Found Current Lease"); + for (ip, (mac, _)) in &self.leases { + println!("IP: {:?}, MAC: {:?}", ip, mac); + } + if let Some(ip) = self.current_lease(&in_packet.chaddr) { + println!("Found Current Lease"); reply(server, options::MessageType::Ack, in_packet, &ip); return; } if !&self.available(&in_packet.chaddr, &req_ip) { - println!("Sending Reply to Request"); + println!("Sending Reply to Request"); nak(server, in_packet, "Requested IP not available"); return; } - self.leases.insert(req_ip, (in_packet.chaddr, Some(Instant::now().add(self.lease_duration)))); - println!("Sending Reply to Request"); + self.leases.insert( + req_ip, + ( + in_packet.chaddr, + Some(Instant::now().add(self.lease_duration)), + ), + ); + println!("Sending Reply to Request"); reply(server, options::MessageType::Ack, in_packet, &req_ip); } @@ -159,8 +163,7 @@ impl MyServer { None => true, } } - fn current_lease(&self, chaddr: &[u8; 6]) -> Option { - + fn current_lease(&self, chaddr: &[u8; 6]) -> Option { for (i, v) in &self.leases { if v.0 == *chaddr { return Some(*i); diff --git a/src/options.rs b/src/options.rs index e2508e5..742f493 100644 --- a/src/options.rs +++ b/src/options.rs @@ -364,4 +364,4 @@ impl MessageType { _ => Err(format!("Invalid DHCP Message Type: {:?}", val)), } } -} \ No newline at end of file +} diff --git a/src/packet.rs b/src/packet.rs index be1d269..8984204 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -19,8 +19,6 @@ pub enum ErrorKind { type IResult = Result<(I, O), CustomErr>; - - /// DHCP Packet Structure #[derive(Debug)] pub struct Packet { @@ -75,7 +73,7 @@ where remaining = input; } Err(CustomErr::NomError(_)) => return Ok((remaining, acc)), - Err(e) => return Err(e), + Err(e) => return Err(e), } } } @@ -85,12 +83,14 @@ pub fn decode_option(input: &[u8]) -> IResult<&[u8], DhcpOption> { assert!(code != END); let (input, len) = custom_be_u8(input)?; - let (input, data) = custom_take(len.into())(input)?; + let (input, data) = custom_take(len.into())(input)?; let option = match code { - DHCP_MESSAGE_TYPE => DhcpOption::DhcpMessageType(match MessageType::from(custom_be_u8(data)?.1) { - Ok(x) => x, - Err(_) => return Err(CustomErr::UnrecognizedMessageType), - }), + DHCP_MESSAGE_TYPE => { + DhcpOption::DhcpMessageType(match MessageType::from(custom_be_u8(data)?.1) { + Ok(x) => x, + Err(_) => return Err(CustomErr::UnrecognizedMessageType), + }) + } SERVER_IDENTIFIER => DhcpOption::ServerIdentifier(decode_ipv4(data)?.1), PARAMETER_REQUEST_LIST => DhcpOption::ParameterRequestList(data.to_vec()), REQUESTED_IP_ADDRESS => DhcpOption::RequestedIpAddress(decode_ipv4(data)?.1), @@ -174,7 +174,6 @@ fn decode(input: &[u8]) -> IResult<&[u8], Packet> { let (input, giaddr) = decode_ipv4(input)?; if hlen != 6 { - return Err(CustomErr::InvalidHlen); } let (_, chaddr) = custom_take(6usize)(input)?; @@ -182,7 +181,6 @@ fn decode(input: &[u8]) -> IResult<&[u8], Packet> { let input = options_input; let (input, _) = custom_tag(&COOKIE)(input)?; - let mut options = Vec::new(); let mut rest = input; @@ -223,7 +221,6 @@ fn decode(input: &[u8]) -> IResult<&[u8], Packet> { impl Packet { pub fn from(input: &[u8]) -> Result> { - Ok(decode(input)?.1) } @@ -248,58 +245,58 @@ impl Packet { None => Err("Packet does not have MessageType option".to_string()), } } - pub fn encode<'a>(&'a self, p: &'a mut [u8]) -> &[u8] { - let broadcast_flag = if self.broadcast { 128 } else { 0 }; - let mut length = 240; + pub fn encode<'a>(&'a self, p: &'a mut [u8]) -> &[u8] { + let broadcast_flag = if self.broadcast { 128 } else { 0 }; + let mut length = 240; - p[..12].copy_from_slice(&[ - if self.reply { BOOT_REPLY } else { BOOT_REQUEST }, - 1, - 6, - self.hops, - ((self.xid >> 24) & 0xFF) as u8, - ((self.xid >> 16) & 0xFF) as u8, - ((self.xid >> 8) & 0xFF) as u8, - (self.xid & 0xFF) as u8, - (self.secs >> 8) as u8, - (self.secs & 255) as u8, - broadcast_flag, - 0, - ]); + p[..12].copy_from_slice(&[ + if self.reply { BOOT_REPLY } else { BOOT_REQUEST }, + 1, + 6, + self.hops, + ((self.xid >> 24) & 0xFF) as u8, + ((self.xid >> 16) & 0xFF) as u8, + ((self.xid >> 8) & 0xFF) as u8, + (self.xid & 0xFF) as u8, + (self.secs >> 8) as u8, + (self.secs & 255) as u8, + broadcast_flag, + 0, + ]); - p[12..16].copy_from_slice(&self.ciaddr.octets()); - p[16..20].copy_from_slice(&self.yiaddr.octets()); - p[20..24].copy_from_slice(&self.siaddr.octets()); - p[24..28].copy_from_slice(&self.giaddr.octets()); - p[28..34].copy_from_slice(&self.chaddr); - p[34..236].fill(0); - p[236..240].copy_from_slice(&COOKIE); + p[12..16].copy_from_slice(&self.ciaddr.octets()); + p[16..20].copy_from_slice(&self.yiaddr.octets()); + p[20..24].copy_from_slice(&self.siaddr.octets()); + p[24..28].copy_from_slice(&self.giaddr.octets()); + p[28..34].copy_from_slice(&self.chaddr); + p[34..236].fill(0); + p[236..240].copy_from_slice(&COOKIE); - for option in &self.options { - let option = option.to_raw(); - let option_len = option.data.len(); - if length + 2 + option_len >= 272 { - break; + for option in &self.options { + let option = option.to_raw(); + let option_len = option.data.len(); + if length + 2 + option_len >= 272 { + break; + } + if let Some(dest) = p.get_mut(length..length + 2 + option_len) { + dest[0] = option.code; + dest[1] = option_len as u8; + dest[2..].copy_from_slice(&option.data); + } + length += 2 + option_len; } - if let Some(dest) = p.get_mut(length..length + 2 + option_len) { - dest[0] = option.code; - dest[1] = option_len as u8; - dest[2..].copy_from_slice(&option.data); + + if let Some(end_segment) = p.get_mut(length..length + 1) { + end_segment[0] = END; } - length += 2 + option_len; - } + length += 1; - if let Some(end_segment) = p.get_mut(length..length + 1) { - end_segment[0] = END; - } - length += 1; + if let Some(pad_segment) = p.get_mut(length..272) { + pad_segment.fill(PAD); + } - if let Some(pad_segment) = p.get_mut(length..272) { - pad_segment.fill(PAD); + &p[..length] } - - &p[..length] -} } const COOKIE: [u8; 4] = [99, 130, 83, 99]; diff --git a/src/server.rs b/src/server.rs index 480b315..ee13473 100644 --- a/src/server.rs +++ b/src/server.rs @@ -74,7 +74,7 @@ impl Server { pub fn serve( udp_soc: UdpSocket, server_ip: Ipv4Addr, - broadcast_ip: Ipv4Addr, + broadcast_ip: Ipv4Addr, mut handler: H, ) -> std::io::Error { let mut in_buf: [u8; 1500] = [0; 1500]; @@ -82,9 +82,8 @@ impl Server { out_buf: Cell::new([0; 1500]), socket: udp_soc, server_ip, - broadcast_ip, + broadcast_ip, src: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), - }; loop { match s.socket.recv_from(&mut in_buf) { @@ -92,7 +91,7 @@ impl Server { Ok((l, src)) => { if let Ok(p) = Packet::from(&in_buf[..l]) { s.src = src; - + handler.handle_request(&s, p); } } @@ -154,21 +153,19 @@ impl Server { /// Checks the packet see if it was intended for this DHCP server (as opposed to some other also on the network). pub fn for_this_server(&self, packet: &Packet) -> bool { match packet.option(options::SERVER_IDENTIFIER) { - Some(DhcpOption::ServerIdentifier(x)) => { - x == &self.server_ip - }, + Some(DhcpOption::ServerIdentifier(x)) => x == &self.server_ip, _ => false, } } -/// Encodes and sends a DHCP packet back to the client. -pub fn send(&self, p: Packet) -> std::io::Result { - let mut addr = self.src; - if p.broadcast || addr.ip() == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) { - addr.set_ip(std::net::IpAddr::V4(self.broadcast_ip)); - } - println!("Sending Response to: {:?}", addr); // Print the address + /// Encodes and sends a DHCP packet back to the client. + pub fn send(&self, p: Packet) -> std::io::Result { + let mut addr = self.src; + if p.broadcast || addr.ip() == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) { + addr.set_ip(std::net::IpAddr::V4(self.broadcast_ip)); + } + println!("Sending Response to: {:?}", addr); // Print the address - self.socket.send_to(p.encode(&mut self.out_buf.get()), addr) -} + self.socket.send_to(p.encode(&mut self.out_buf.get()), addr) + } } From 46220b3809295adef3623cf27cc6b7636f087d11 Mon Sep 17 00:00:00 2001 From: junderw Date: Mon, 30 Oct 2023 20:18:33 -0700 Subject: [PATCH 3/3] Fix clippy lints --- src/packet.rs | 24 +++++++----------------- src/server.rs | 18 +++++++++--------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/packet.rs b/src/packet.rs index 8984204..2d41431 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -133,7 +133,7 @@ fn custom_tag<'a>(tag: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], } } fn custom_be_u8(input: &[u8]) -> IResult<&[u8], u8> { - if input.len() < 1 { + if input.is_empty() { return Err(CustomErr::InvalidHlen); } @@ -184,16 +184,11 @@ fn decode(input: &[u8]) -> IResult<&[u8], Packet> { let mut options = Vec::new(); let mut rest = input; - loop { - match decode_option(rest) { - Ok((new_rest, option)) => { - rest = new_rest; - options.push(option); - if rest.starts_with(&[END]) { - break; - } - } - Err(_) => break, + while let Ok((new_rest, option)) = decode_option(rest) { + rest = new_rest; + options.push(option); + if rest.starts_with(&[END]) { + break; } } @@ -226,12 +221,7 @@ impl Packet { /// Extracts requested option payload from packet if available pub fn option(&self, code: u8) -> Option<&DhcpOption> { - for option in &self.options { - if option.code() == code { - return Some(&option); - } - } - None + self.options.iter().find(|&option| option.code() == code) } /// Convenience function for extracting a packet's message type. diff --git a/src/server.rs b/src/server.rs index ee13473..c1d4802 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,5 @@ +//! This is a convenience module that simplifies the writing of a DHCP server service. + use std::cell::Cell; use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; @@ -5,8 +7,6 @@ use crate::options; use crate::options::{DhcpOption, MessageType}; use crate::packet::*; -///! This is a convenience module that simplifies the writing of a DHCP server service. - pub struct Server { out_buf: Cell<[u8; 1500]>, socket: UdpSocket, @@ -22,12 +22,12 @@ pub trait Handler { pub fn filter_options_by_req(opts: &mut Vec, req_params: &[u8]) { let mut pos = 0; let h = &[ - options::DHCP_MESSAGE_TYPE as u8, - options::SERVER_IDENTIFIER as u8, - options::SUBNET_MASK as u8, - options::IP_ADDRESS_LEASE_TIME as u8, - options::DOMAIN_NAME_SERVER as u8, - options::ROUTER as u8, + options::DHCP_MESSAGE_TYPE, + options::SERVER_IDENTIFIER, + options::SUBNET_MASK, + options::IP_ADDRESS_LEASE_TIME, + options::DOMAIN_NAME_SERVER, + options::ROUTER, ] as &[u8]; // Process options from req_params @@ -132,7 +132,7 @@ impl Server { if let Some(DhcpOption::ParameterRequestList(prl)) = req_packet.option(options::PARAMETER_REQUEST_LIST) { - filter_options_by_req(&mut opts, &prl); + filter_options_by_req(&mut opts, prl); } self.send(Packet {