use std::net::UdpSocket; use std::net; use std::str; use std::fmt; use std::mem; const BUFFER_SIZE: usize = 5000; const SERVER_PORT: u16 = 67; const IP_SERVER: net::Ipv4Addr = net::Ipv4Addr::new(10, 2, 0, 11); enum DhcpMessageType { DHCPDISCOVER = 1, // Send by client DHCPOFFER = 2, // Send by server DHCPREQUEST = 3, // Send by client DHCPDECLINE, // Send by client DHCPPACK, // Send by server DHCPNAK, // Send by server DHCPRELEASE, // Send by client DHCPINFORM // Not send } const OPTIONS_NAME : [&str;77] = ["Pad", // 0 "Subnet mask", "Time offset", "Router option", "Time server", "Name server", // 5 "Domain name server", "Log server", "Cookie server 🍪", "LPR server", "Impress server", // 10 "Ressource location server", "Host name", "Boot file size", "Merit dump file", "Domain name", // 15 "Swap server", "Root path", "Extensions path", "IP Forwarding", "Non-local source routing", // 20 "Policy filter", "Maximum datagram", "Default TTL", "Path MTU again", "Path MTU plateau", // 25 "Interface MTU", "All subnets are local", "Broadcast address", "Perform mask discovery", "Mask supplier", // 30 "Perform router discovery", "Router solicitation address", "Static route", "Trailer Encapsulation", "ARP cache timeout", // 35 "Ethernet encapsulation", "TCP Default TTL", "TCP Keepalive interval", "TCP Keepalive garbage", "Network information service domain", // 40 "Network information servers", "Network time protocol servers", "Vendor specific information", "NetBIOS over TCP/IP Name server", "NetBIOS over TCP/IP Datagram disctribution server", // 45 "NetBIOS over TCP/IP Node type", "NetBIOS over TCP/IP Scope", "X Window System Font Server", "X Window System Display Manager", "Requested IP Address", // 50 "IP Address Lease Time", "Option Overload", "DHCP Message Type", "Server Identifier", "Parameter Request List", // 55 "Message", "Maximum DHCP Message Size", "Renewal T1 Time Value", "Rebindin T2 Time Value", "Vendor class identifier", // 60 "Client-identifier", "62 - Unused", "63 - Unused", "Network Information Service+ Domain", "Network Information Service+ Servers", // 65 "TFTP Server Option", "Bootfile name", "Mobile IP Home Agent", "SMTP Server", "POP3 Server", // 70 "NNTP Server", "WWW Server", "Finger Server", "IRC Server", "StreetTalk Server", // 75 "STDA Server", ]; struct DhcpMessage { op: u8, htype: u8, hlen: u8, hops: u8, xid: u32, secs: u16, flags: u16, ciaddr: u32, yiaddr: u32, siaddr: u32, giaddr: u32, chaddr: [u8; 6], sname: [u8; 64], file: [u8; 128], options: [u8; 312] } impl DhcpMessage { fn request_type( &self ) -> Option { let mut i = 4; // Magic cookie ! 🍪 loop { if i >= 312 { return None; } let i_tmp : usize = usize::from(i); if self.options[i_tmp] == 53 { if self.options[i_tmp+2] > 0 && self.options[i_tmp+2] < 9 { print!("Type: {}\n", self.options[i_tmp+2]); return Some(unsafe { mem::transmute(self.options[i_tmp+2]) }); } else { return None } } else if self.options[i_tmp] == 0 || self.options[i_tmp] == 255 { i += 1 } else if self.options[i_tmp] < 77 { i += u16::from(self.options[i_tmp+1]); } else { return None; } } } } impl fmt::Display for DhcpMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut name: &str = "Undef"; let mut is_file_empty = true; for o in &self.file[..] { if *o != 0 { is_file_empty = false; } }; let is_file_empty = is_file_empty; if let Ok(s) = std::str::from_utf8(&self.sname[..]) { name = s; } write!(f, "( op: {:02X} htype: {:02X} hlen: {:02X} hops: {:02X} xid: {} secs: {:04X} flags: {:04X} ciaddr: {:#08X} yiaddr: {:#08X} siaddr: {:#08X} giaddr: {:#08X} chaddr: {:02X}{:02X}{:02X}{:02X}{:02X}{:02X} sname: [{}] file: {} ", self.op, self.htype, self.hlen, self.hops, self.xid, self.secs, self.flags, self.ciaddr, self.yiaddr, self.siaddr, self.giaddr, self.chaddr[0], self.chaddr[1], self.chaddr[2], self.chaddr[3], self.chaddr[4], self.chaddr[5], name, if is_file_empty { "empty" } else {"not empty"})?; write!(f, "options :\n(\n")?; let mut i: u16 = 0; { // Check for Magic Cookie 🍪 let i_tmp : usize = usize::from(i); if self.options[i_tmp] == 0x63 && self.options[i_tmp+1] == 0x82 && self.options[i_tmp+2] == 0x53 && self.options[i_tmp+3] == 0x63 { write!(f, " MAGIC COOKIE 0x63825363\n")?; i += 4 } } loop { if i >= 312 { break; } let i_tmp : usize = usize::from(i); if self.options[i_tmp] == 0xFF { write!(f, " End Option(0xFF)\n")?; break; } else if self.options[i_tmp] == 0 { let mut times: u16= 1; while i_tmp + usize::from(times) < 312 && self.options[i_tmp+usize::from(times)] == 0 { times += 1; } write!(f, " Pad Option(0x00) x{}\n", times)?; i += times; } else if self.options[i_tmp] < 77 { write!(f, " {} Option({:02x})\n", OPTIONS_NAME[usize::from(self.options[i_tmp])], self.options[i_tmp])?; i += u16::from(self.options[i_tmp+1]); } else { write!(f, " Unknow Option({:02x})\n", self.options[i_tmp])?; break; } } write!(f, ")\n") } } impl DhcpMessage { pub fn new() -> DhcpMessage { DhcpMessage { op: 0, htype: 0, hlen: 0, hops: 0, xid: 0, secs: 0, flags: 0, ciaddr: 0, yiaddr: 0, siaddr: 0, giaddr: 0, chaddr: [0; 6], sname: [0; 64], file: [0; 128], options: [0; 312], } } } fn fill_dhcp_message( dhcp_message: &mut DhcpMessage, buf : &[u8; BUFFER_SIZE], size: usize) -> bool { dhcp_message.op = buf[0]; dhcp_message.htype = buf[1]; dhcp_message.hlen = buf[2]; dhcp_message.hops = buf[3]; dhcp_message.xid = u32::from_be_bytes([buf[4], buf[5], buf[ 6], buf[ 7]]); dhcp_message.secs = u16::from_be_bytes([buf[ 8], buf[ 9]]); dhcp_message.flags = u16::from_be_bytes([buf[10], buf[11]]); dhcp_message.ciaddr = u32::from_be_bytes([buf[12], buf[13], buf[14], buf[15]]); dhcp_message.yiaddr = u32::from_be_bytes([buf[16], buf[17], buf[18], buf[19]]); dhcp_message.siaddr = u32::from_be_bytes([buf[20], buf[21], buf[22], buf[23]]); dhcp_message.giaddr = u32::from_be_bytes([buf[24], buf[25], buf[26], buf[27]]); dhcp_message.chaddr = [buf[28], buf[29], buf[30], buf[31], buf[32], buf[33]]; for i in 0..64 { dhcp_message.sname[i] = buf[i+34]; } for i in 0..128 { dhcp_message.file[i] = buf[i+98]; } let size = if size > 538 { 538 } else { size }; for i in 236..size { dhcp_message.options[i-236] = buf[i]; } true } fn main() -> std::io::Result<()> { { let addrs = [ net::SocketAddr::from(([0, 0, 0, 0], SERVER_PORT)), ]; let socket = UdpSocket::bind(&addrs[..])?; loop { print!("Ready to listen ! 🦊\n"); // Receives a single datagram message on the socket. If `buf` is too small to hold // the message, it will be cut off. let mut buf = [0; BUFFER_SIZE]; let mut dhcp_message: DhcpMessage = DhcpMessage::new(); let (amt, _) = socket.recv_from(&mut buf)?; print!("Receive {} bytes\n", amt); for i in 0..amt { if i % 16 == 0 { print!("\n"); } print!("{:02x}", buf[i]) } fill_dhcp_message(&mut dhcp_message, &buf, amt); print!("\n"); print!("{}\n", dhcp_message); if dhcp_message.xid == 42 { break } if let Some(DhcpMessageType::DHCPDISCOVER) = dhcp_message.request_type() { print!("It's a discovery !\n"); } else { print!("It's not a discovery !\n"); } } } // the socket is closed here Ok(()) }