diff --git a/examples/monitor.rs b/examples/monitor.rs deleted file mode 100644 index 1a07212..0000000 --- a/examples/monitor.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate dhcp4r; -extern crate time; - -use dhcp4r::{options, packet, server}; -use std::net::{Ipv4Addr, UdpSocket}; - -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 {}, - ); -} - -struct MyServer {} - -impl server::Handler for MyServer { - fn handle_request(&mut self, _: &server::Server, in_packet: packet::Packet) { - match in_packet.message_type() { - Ok(options::MessageType::Request) => { - let req_ip = match in_packet.option(options::REQUESTED_IP_ADDRESS) { - Some(options::DhcpOption::RequestedIpAddress(x)) => x.clone(), - _ => in_packet.ciaddr, - }; - println!( - "{}\t{}\t{}\tOnline", - time::OffsetDateTime::try_now_local() - .unwrap() - .format("%Y-%m-%dT%H:%M:%S"), - chaddr(&in_packet.chaddr), - Ipv4Addr::from(req_ip) - ); - } - _ => {} - } - } -} - -/// Formats byte array machine address into hex pairs separated by colons. -/// Array must be at least one byte long. -fn chaddr(a: &[u8]) -> String { - a[1..].iter().fold(format!("{:02x}", a[0]), |acc, &b| { - format!("{}:{:02x}", acc, &b) - }) -} diff --git a/examples/server.rs b/examples/server.rs deleted file mode 100644 index ea70b40..0000000 --- a/examples/server.rs +++ /dev/null @@ -1,202 +0,0 @@ -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 dhcp4r::{options, packet, server}; - -// Server configuration -const SERVER_IP: Ipv4Addr = Ipv4Addr::new(192, 168, 2, 1); -const IP_START: [u8; 4] = [192, 168, 2, 2]; -const SUBNET_MASK: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0); -const DNS_IPS: [Ipv4Addr; 1] = [ - // Google DNS servers - Ipv4Addr::new(8, 8, 8, 8), -]; -const ROUTER_IP: Ipv4Addr = Ipv4Addr::new(192, 168, 2, 1); -const BROADCAST_IP: Ipv4Addr = Ipv4Addr::new(192, 168, 2, 255); -const LEASE_DURATION_SECS: u32 = 86400; -const LEASE_NUM: u32 = 252; - -// Derived constants -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(); - // 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 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)); - } - } - } - } - } else { - eprintln!("Failed to open leases file. Continuing..."); - //return; - } - - let ms = MyServer { - leases, - last_lease: 0, - lease_duration: Duration::new(LEASE_DURATION_SECS as u64, 0), - }; - - server::Server::serve(socket, SERVER_IP, BROADCAST_IP, ms); -} - -struct MyServer { - leases: HashMap)>, // Ipv4Addr -> (MAC address, lease duration) mapping - last_lease: u32, - lease_duration: Duration, -} - -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"); - reply(server, options::MessageType::Offer, in_packet, &ip); - return; - } - // Otherwise choose a free ip if available - for _ in 0..LEASE_NUM { - self.last_lease = (self.last_lease + 1) % LEASE_NUM; - if self.available( - &in_packet.chaddr, - &((IP_START_NUM + &self.last_lease).into()), - ) { - println!("Sending Reply to discover"); - reply( - server, - options::MessageType::Offer, - in_packet, - &((IP_START_NUM + &self.last_lease).into()), - ); - break; - } - } - } - - Ok(options::MessageType::Request) => { - // Ignore requests to alternative DHCP server - if !server.for_this_server(&in_packet) { - //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"); - reply(server, options::MessageType::Ack, in_packet, &ip); - return; - } - if !&self.available(&in_packet.chaddr, &req_ip) { - 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"); - reply(server, options::MessageType::Ack, in_packet, &req_ip); - } - - Ok(options::MessageType::Release) | Ok(options::MessageType::Decline) => { - // Ignore requests to alternative DHCP server - if !server.for_this_server(&in_packet) { - return; - } - if let Some(ip) = self.current_lease(&in_packet.chaddr) { - self.leases.remove(&ip); - } - } - - // TODO - not necessary but support for dhcp4r::INFORM might be nice - _ => {} - } - } -} - -impl MyServer { - fn available(&self, chaddr: &[u8; 6], addr: &Ipv4Addr) -> bool { - let pos: u32 = (*addr).into(); - pos >= IP_START_NUM - && pos < IP_START_NUM + LEASE_NUM - && match self.leases.get(addr) { - Some((mac, expiry)) => { - *mac == *chaddr || expiry.map_or(true, |exp| Instant::now().gt(&exp)) - } - None => true, - } - } - fn current_lease(&self, chaddr: &[u8; 6]) -> Option { - for (i, v) in &self.leases { - if v.0 == *chaddr { - return Some(*i); - } - } - None - } -} - -fn reply( - s: &server::Server, - msg_type: options::MessageType, - req_packet: packet::Packet, - offer_ip: &Ipv4Addr, -) { - let _ = s.reply( - msg_type, - vec![ - options::DhcpOption::IpAddressLeaseTime(LEASE_DURATION_SECS), - options::DhcpOption::SubnetMask(SUBNET_MASK), - options::DhcpOption::Router(vec![ROUTER_IP]), - options::DhcpOption::DomainNameServer(DNS_IPS.to_vec()), - ], - *offer_ip, - req_packet, - ); -} - -fn nak(s: &server::Server, req_packet: packet::Packet, message: &str) { - let _ = s.reply( - options::MessageType::Nak, - vec![options::DhcpOption::Message(message.to_string())], - Ipv4Addr::new(0, 0, 0, 0), - req_packet, - ); -}