use std::net::UdpSocket; use std::net; use std::str; use std::fmt; use std::mem; use std::net::Ipv4Addr; use std::io::ErrorKind; 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 } impl fmt::Display for DhcpMessageType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { DhcpMessageType::DHCPDISCOVER => "DHCPDISCOVER", DhcpMessageType::DHCPOFFER => "DHCPOFFER", DhcpMessageType::DHCPREQUEST => "DHCPREQUEST", DhcpMessageType::DHCPNAK => "DHCPNAK", DhcpMessageType::DHCPDECLINE => "DHCPDECLINE", DhcpMessageType::DHCPPACK => "DHCPPACK", DhcpMessageType::DHCPRELEASE => "DHCPRELEASE", DhcpMessageType::DHCPINFORM => "DHCPINFORM" }; write!(f, "{}", s) } } impl DhcpMessageType { fn into( i: u8 ) -> DhcpMessageType { match(i) { 1 => DhcpMessageType::DHCPDISCOVER, 2 => DhcpMessageType::DHCPOFFER, 3 => DhcpMessageType::DHCPREQUEST, _ => DhcpMessageType::DHCPNAK, } } } struct Config { ranges: Vec<(Ipv4Addr, Ipv4Addr)>, name: String, } 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 get_option_data( &self, opt: u8) -> Option<&[u8]> { if opt == 0 || opt == 255 { return None; } let mut i = 4; // Magic cookie ! 🍪 loop { if i >= 312 { return None; } let i_tmp : usize = usize::from(i); if self.options[i_tmp] == opt { return Some(&self.options[i_tmp..(self.options[i_tmp+1] as usize + 2 + i_tmp)]); } 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]) + 2; } else { return None; } } } 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 { 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]) + 2; } else { return None; } } } fn to_bytes( &self ) -> Vec { let mut ret = Vec::new(); ret } /* * *use std::net::UdpSocket; let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data"); * * * */ } 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]) + 2; } 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 deal_discovery(dhcp_message: &mut DhcpMessage, conf: &Config) -> Result { println!("Discovery ! 🦊"); Err(ErrorKind::NotFound) } fn is_in_range(ip: Ipv4Addr, range: &Vec<(Ipv4Addr,Ipv4Addr)>) -> bool { for (min, max) in range { if ip >= *min && ip <= *max { return true } } return false } fn deal_request(dhcp_message: &DhcpMessage, conf: &Config, leases: &Vec) -> Result { println!("Request ! 🦊"); let request_opt = dhcp_message.get_option_data(50); let mut ret: DhcpMessage = DhcpMessage { op: dhcp_message.op, htype: dhcp_message.op, hlen: dhcp_message.hlen, hops: dhcp_message.hops, xid: dhcp_message.xid, secs: 0, flags: dhcp_message.flags, ciaddr: 0, siaddr: 0, giaddr: 0, yiaddr: 0, chaddr: dhcp_message.chaddr, sname: [0; 64], file: dhcp_message.file, options: dhcp_message.options, }; let request_opt = if let Some(request_opt) = request_opt { if request_opt.len() < 6 { println!("Too short {:?}", request_opt); return Err(ErrorKind::InvalidData); } request_opt } else { println!("Didn't get anything 🤷"); return Err(ErrorKind::InvalidData); }; let requested_addr = net::Ipv4Addr::new(request_opt[2], request_opt[3], request_opt[4], request_opt[5]); println!("Want: {}", requested_addr); if leases.contains(&requested_addr) { // Send DHCPNAK return Err(ErrorKind::AddrInUse); } if !is_in_range(requested_addr, &conf.ranges) { // Send DHCPDECLINE return Err(ErrorKind::AddrNotAvailable); } ret.yiaddr = u32::from(requested_addr); // Send DHCPPACK Err(ErrorKind::NotFound) } fn main() -> std::io::Result<()> { let mut available_ips = Vec::new(); available_ips.push((Ipv4Addr::new(10, 0, 0, 0), Ipv4Addr::new(11, 0, 0, 0))); let config = Config { ranges: available_ips, name: "Unibit".to_string(), }; let mut leases: Vec = Vec::new(); { 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); println!(""); println!("{}", dhcp_message); if dhcp_message.xid == 42 { break } let r = match dhcp_message.request_type() { Some(DhcpMessageType::DHCPDISCOVER) => deal_discovery(&mut dhcp_message, &config), Some(DhcpMessageType::DHCPREQUEST) => deal_request(&mut dhcp_message, &config, &leases), Some(DhcpMessageType::DHCPOFFER) => Err(ErrorKind::PermissionDenied), Some(e) => { println!("Its {} !", e); Err(ErrorKind::InvalidData) }, _ => Err(ErrorKind::InvalidData) }; } } // the socket is closed here Ok(()) }