From 0aa92d80215f21398dc55f7edc8c93841ae4d374 Mon Sep 17 00:00:00 2001 From: Xnoe Date: Fri, 23 Sep 2022 13:58:17 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 1 + Cargo.lock | 113 +++ Cargo.toml | 16 + LICENSE | 19 + README.md | 11 + src/bootp.rs | 192 +++++ src/dhcp.rs | 1809 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 15 + src/udpbuilder.rs | 175 +++++ 9 files changed, 2351 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/bootp.rs create mode 100644 src/dhcp.rs create mode 100644 src/lib.rs create mode 100644 src/udpbuilder.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e810dca --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "dhcprs" +version = "0.1.0" +dependencies = [ + "eui48", + "libc", + "nix", +] + +[[package]] +name = "eui48" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887418ac5e8d57c2e66e04bdc2fe15f9a5407be20b54a82c86bd0e368b709701" +dependencies = [ + "regex", + "rustc-serialize", +] + +[[package]] +name = "libc" +version = "0.2.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5192e5c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "dhcprs" +version = "0.1.0" +edition = "2021" +description = "A library for encoding and decoding DHCP/BOOTP packets" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "dhcprs" +path = "src/lib.rs" + +[dependencies] +nix = "0.25.0" +eui48 = "1.1.0" +libc = "0.2.133" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e20ca3c --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 xnoe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..20a13a4 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# dhcprs + +DHCP/BOOTP packet encode/decode library. + +The purpose of this library is to provide an easy to use interface over +DHCP, BOOTP and UDP packets to enable the creation of programs that +make use of DHCP. + +BOOTP specific functionality is provided by the `dhcprs::bootp` module. +DHCP specific functionality is provided by the `dhcprs::dhcp` module. +There is additionally simple UDP packet encode/decode provided by the `dhcprs::udpbuilder`. \ No newline at end of file diff --git a/src/bootp.rs b/src/bootp.rs new file mode 100644 index 0000000..ee4aced --- /dev/null +++ b/src/bootp.rs @@ -0,0 +1,192 @@ +use eui48::MacAddress; + +// Raw BOOTP Packet. +// Total size: 546 bytes +#[allow(dead_code)] +#[repr(packed)] +pub struct RawBOOTPPacket { + // RFC951 + op: u8, // OpCode + htype: u8, // Host Type + hlen: u8, // Host Length + hops: u8, // Hops + xid: u32, // Transaction ID + secs: u16, // Seconds since boot + flags: u16, // Unused in BOOTP, flags in DHCP + ciaddr: u32, // Client Address (provided by client) + yiaddr: u32, // Client Address (provided by server) + siaddr: u32, // Server Address + giaddr: u32, // Gateway Address + chaddr: [u8; 16], // Client Hardware (MAC) Address + sname: [u8; 64], // Server hostname, null terminated string + file: [u8; 128], // Boot file name, null terminated string + vend: [u8; 312], // Vendor specific data. In RFC2131, this is required to be 312 octets for DHCP. +} + +impl RawBOOTPPacket { + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, 546) } + } +} + +pub enum OpCode { + BOOTREQUEST, + BOOTREPLY, +} + +impl TryFrom for OpCode { + type Error = (); + fn try_from(item: u8) -> Result { + match item { + 1 => Ok(Self::BOOTREQUEST), + 2 => Ok(Self::BOOTREPLY), + _ => Err(()), + } + } +} + +impl TryFrom for u8 { + type Error = (); + fn try_from(item: OpCode) -> Result { + match item { + OpCode::BOOTREQUEST => Ok(1), + OpCode::BOOTREPLY => Ok(2), + } + } +} + +// Higher level BOOTP Packet representation +pub struct BOOTPPacket { + pub op: OpCode, + pub hops: u8, + pub xid: u32, + pub secs: u16, + pub flags: u16, + pub ciaddr: Option, + pub yiaddr: Option, + pub siaddr: Option, + pub giaddr: Option, + pub chaddr: MacAddress, + pub sname: [u8; 64], + pub file: [u8; 128], + vend: [u8; 312], +} + +impl From for RawBOOTPPacket { + fn from(item: BOOTPPacket) -> Self { + let mut chaddr: [u8; 16] = [0; 16]; + chaddr[..6].copy_from_slice(item.chaddr.as_bytes()); + Self { + op: item.op.try_into().unwrap(), + htype: 1, + hlen: 6, + hops: item.hops, + xid: item.xid, + secs: item.secs, + flags: item.flags, + ciaddr: if item.ciaddr.is_some() { + item.ciaddr.unwrap().into() + } else { + 0 + }, + yiaddr: if item.yiaddr.is_some() { + item.yiaddr.unwrap().into() + } else { + 0 + }, + siaddr: if item.siaddr.is_some() { + item.siaddr.unwrap().into() + } else { + 0 + }, + giaddr: if item.giaddr.is_some() { + item.giaddr.unwrap().into() + } else { + 0 + }, + chaddr: chaddr, + sname: item.sname, + file: item.file, + vend: item.vend, + } + } +} + +impl From for BOOTPPacket { + fn from(item: RawBOOTPPacket) -> Self { + Self { + op: item.op.try_into().unwrap(), + hops: item.hops, + xid: item.xid, + secs: item.secs, + flags: item.flags, + ciaddr: if item.ciaddr == 0 { + None + } else { + Some(std::net::Ipv4Addr::from(item.ciaddr)) + }, + yiaddr: if item.yiaddr == 0 { + None + } else { + Some(std::net::Ipv4Addr::from(item.yiaddr)) + }, + siaddr: if item.siaddr == 0 { + None + } else { + Some(std::net::Ipv4Addr::from(item.siaddr)) + }, + giaddr: if item.giaddr == 0 { + None + } else { + Some(std::net::Ipv4Addr::from(item.giaddr)) + }, + chaddr: MacAddress::from_bytes(&item.chaddr[0..6]).unwrap(), + sname: item.sname, + file: item.file, + vend: item.vend, + } + } +} + +impl BOOTPPacket { + /// Create a new BOOTP packet. + /// + /// dhcprs encourages the use of higher level interfaces which are then + /// converted to lower level (packed) structures that can be serialised + /// in to bytes and then sent down a socket. + pub fn new( + op: OpCode, + hops: u8, + xid: u32, + secs: u16, + flags: u16, + ciaddr: Option, + yiaddr: Option, + siaddr: Option, + giaddr: Option, + chaddr: MacAddress, + sname: [u8; 64], + file: [u8; 128], + vend: [u8; 312], + ) -> Self { + Self { + op: op, + hops: hops, + xid: xid, + secs: secs, + flags: flags, + ciaddr: ciaddr, + yiaddr: yiaddr, + siaddr: siaddr, + giaddr: giaddr, + chaddr: chaddr, + sname: sname, + file: file, + vend: vend, + } + } + + pub fn get_vend(&self) -> &[u8] { + return &self.vend; + } +} diff --git a/src/dhcp.rs b/src/dhcp.rs new file mode 100644 index 0000000..87fdc61 --- /dev/null +++ b/src/dhcp.rs @@ -0,0 +1,1809 @@ +use std::net::Ipv4Addr; + +/// Enum representing the parameter for DHCP Option 53 "Message Type". +#[derive(Debug)] +pub enum DHCPMessageType { + // RFC1533 + DHCPDiscover, + DHCPOffer, + DHCPRequest, + DHCPDecline, + DHCPACK, + DHCPNAK, + DHCPRelease, + + // RFC2132 + DHCPInform, +} + +impl TryFrom for DHCPMessageType { + type Error = (); + fn try_from(item: u8) -> Result { + match item { + 1 => Ok(DHCPMessageType::DHCPDiscover), + 2 => Ok(DHCPMessageType::DHCPOffer), + 3 => Ok(DHCPMessageType::DHCPRequest), + 4 => Ok(DHCPMessageType::DHCPDecline), + 5 => Ok(DHCPMessageType::DHCPACK), + 6 => Ok(DHCPMessageType::DHCPNAK), + 7 => Ok(DHCPMessageType::DHCPRelease), + 8 => Ok(DHCPMessageType::DHCPInform), + _ => Err(()), + } + } +} + +impl From for u8 { + fn from(item: DHCPMessageType) -> Self { + match item { + DHCPMessageType::DHCPDiscover => 1, + DHCPMessageType::DHCPOffer => 2, + DHCPMessageType::DHCPRequest => 3, + DHCPMessageType::DHCPDecline => 4, + DHCPMessageType::DHCPACK => 5, + DHCPMessageType::DHCPNAK => 6, + DHCPMessageType::DHCPRelease => 7, + DHCPMessageType::DHCPInform => 8, + } + } +} + +/// Enum representing the parameter for DHCP Option 46 "NetBIOS over TCP/IP Node Type". +#[derive(Debug)] +pub enum NetBIOSoverTCPIPNodeType { + Bnode, + Pnode, + Mnode, + Hnode, +} + +impl TryFrom for NetBIOSoverTCPIPNodeType { + type Error = (); + fn try_from(item: u8) -> Result { + match item { + 1 => Ok(NetBIOSoverTCPIPNodeType::Bnode), + 2 => Ok(NetBIOSoverTCPIPNodeType::Pnode), + 4 => Ok(NetBIOSoverTCPIPNodeType::Mnode), + 8 => Ok(NetBIOSoverTCPIPNodeType::Hnode), + _ => Err(()), + } + } +} + +impl From for u8 { + fn from(item: NetBIOSoverTCPIPNodeType) -> Self { + match item { + NetBIOSoverTCPIPNodeType::Bnode => 1, + NetBIOSoverTCPIPNodeType::Pnode => 2, + NetBIOSoverTCPIPNodeType::Mnode => 4, + NetBIOSoverTCPIPNodeType::Hnode => 8, + } + } +} + +/// This enum represents all the DHCP options supported by dhcprs +#[derive(Debug)] +pub enum DHCPOption { + // RFC1533 + Pad, // 0 + End, // 255 + SubnetMask(Ipv4Addr), // 1 4 m1 m2 m3 m4 + TimeOffset(u32), // 2 4 n1 n2 n3 n4 + Router(Vec), // 3 n a1 a2 a3 a4 a1 a2 ... + TimeServer(Vec), // 4 n a1 a2 a3 a4 a1 a2 ... + NameServer(Vec), // 5 n a1 a2 a3 a4 a1 a2 ... + DomainNameServer(Vec), // 6 n a1 a2 a3 a4 a1 a2 ... + LogServer(Vec), // 7 n a1 a2 a3 a4 a1 a2 ... + CookieServer(Vec), // 8 n a1 a2 a3 a4 a1 a2 ... + LPRServer(Vec), // 9 n a1 a2 a3 a4 a1 a2 ... + ImpressServer(Vec), // 10 n a1 a2 a3 a4 a1 a2 ... + ResourceLocationServer(Vec), // 11 n a1 a2 a3 a4 a1 a2 ... + HostName(String), // 12 n h1 h2 h3 h4 h5 h6 ... + BootfileSize(u16), // 13 2 l1 l2 + MeritDumpFile(String), // 14 n n1 n2 n3 n4 ... + DomainName(String), // 15 n d1 d2 d3 d4 ... + SwapServer(Ipv4Addr), // 16 4 a1 a2 a3 a4 + RootPath(String), // 17 n n1 n2 n3 n4 ... + ExtensionsPath(String), // 18 n n1 n2 n3 n4 ... + IPForwarding(bool), // 19 1 0/1 + NonLocalSourceRouting(bool), // 20 1 0/1 + PolicyFilter(Vec<(Ipv4Addr, Ipv4Addr)>), // 21 n a1 a2 a3 a4 m1 m2 m3 m4 a1 a2 a3 a4 m1 m2 m3 m4 ... + MaximumDatagramReassemblySize(u16), // 22 2 s1 s2 + DefaultIPTTL(u8), // 23 1 ttl + PathMTUAgingTimeout(u32), // 24 4 t1 t2 t3 t4 + PathMTUPlateaus(Vec), // 25 n s1 s2 s1 s2 ... + InterfaceMTU(u16), // 26 2 m1 m2 + AllSubnetsLocal(bool), // 27 1 0/1 + BroadcastAddress(Ipv4Addr), // 28 4 b1 b2 b3 b4 + PerformMaskDiscovery(bool), // 29 1 0/1 + MaskSupplier(bool), // 30 1 0/1 + PerformRouterDiscovery(bool), // 31 1 0/1 + RouterSolicitationAddress(Ipv4Addr), // 32 4 a1 a2 a3 a4 + StaticRoutes(Vec<(Ipv4Addr, Ipv4Addr)>), // 33 n d1 d2 d3 d4 r1 r2 r3 r4 d1 d2 d3 d4 r1 r2 r3 r4 ... + TrailerEncapsultion(bool), // 34 1 0/1 + ARPCacheTimeout(u32), // 35 4 t1 t2 t3 t4 + EthernetEncapsulation(bool), // 36 1 0/1 + TCPDefaultTTL(u8), // 37 1 n + TCPKeepaliveInterval(u32), // 38 4 t1 t2 t3 t4 + TCPKeepaliveGarbage(bool), // 39 1 0/1 + NISDomain(String), // 40 n n1 n2 n3 n4 ... + NetworkInformationServers(Vec), // 41 n a1 a2 a3 a4 a1 a2 a3 a4 ... + NTPServers(Vec), // 42 n a1 a2 a3 a4 a1 a2 a3 a4 ... + VendorSpecificInformation(Vec), // 43 n i1 i2 ... + NetBIOSoverTCPIPNameServer(Vec), // 44 n a1 a2 a3 a4 b1 b2 b3 b4 ... + NetBIOSoverTCPIPDatagramDistributionServer(Vec), // 45 n a1 a2 a3 a4 b1 b2 b3 b4 ... + NetBIOSoverTCPIPNodeType(NetBIOSoverTCPIPNodeType), // 46 1 n + NetBIOSoverTCPIPScope(Vec), // 47 n s1 s2 s3 s4 ... + XWindowSystemFontServer(Vec), // 48 n a1 a2 a3 a4 a1 a2 ... + XWindowSystemDisplayManager(Vec), // 49 n a2 a3 a4 a4 a1 a2 ... + RequestIPAddress(Ipv4Addr), // 50 4 a1 a2 a3 a4 + IPAddressLeaseTime(u32), // 51 4 t1 t2 t3 t4 + OptionOverload((bool, bool)), // 52 1 1/2/3 (1 -> file overloaded, 2 -> sname overloaded, 3 -> both overloaded) + DHCPMessageType(DHCPMessageType), // 53 1 n + ServerIdentifier(Ipv4Addr), // 54 4 a1 a2 a3 a4 + ParameterRequest(Vec), // 55 n c1 c2 ... + Message(String), // 56 n c1 c2 ... + MaximumDHCPMessageSize(u16), // 57 2 l1 l2 + RenewalTime(u32), // 58 4 t1 t2 t3 t4 + RebindingTime(u32), // 59 4 t1 t2 t3 t4 + ClassIdentifier(Vec), // 60 n i1 i2 ... + ClientIdentifier(Vec), // 61 n t1 i1 i2 ... + + // RFC2132 + NISPlusDomain(String), // 64 n n1 n2 n3 n4 ... + NISPlusServers(Vec), // 65 n a1 a2 a3 a4 a1 a2 ... + TFTPServerName(String), // 66 n c1 c2 c3 ... + BootfileName(String), // 67 n c1 c2 c3 ... + MobileIPHomeAgent(Vec), // 68 n a1 a2 a3 a4 a1 a2 ... + SMTPServer(Vec), // 69 n a1 a2 a3 a4 a1 a2 ... + POP3Server(Vec), // 70 n a1 a2 a3 a4 a1 a2 ... + NNTPServer(Vec), // 71 n a1 a2 a3 a4 a1 a2 ... + DefaultWWWServer(Vec), // 72 n a1 a2 a3 a4 a1 a2 ... + DefaultFingerServer(Vec), // 73 n a1 a2 a3 a4 a1 a2 ... + DefaultIRCServer(Vec), // 74 n a1 a2 a3 a4 a1 a2 ... + StreetTalkServer(Vec), // 75 n a1 a2 a3 a4 a1 a2 ... + STDAServer(Vec), // 76 n a1 a2 a3 a4 a1 a2 ... + + /*// RFC4833 + TimezonePOSIX(String), // 100 N IEEE 1003.1 String + TimezoneDB(String), // 101 N Reference to TZ Database + + // RFC3442 + ClasslessStaticRoute(Vec<(Ipv4Addr, u32, Ipv4Addr)>), // 121 n d1 ... dN r1 r2 r3 r4 d1 ... dN r1 r2 r3 r4*/ + + // Catchall + Option(u8, Vec), +} + +macro_rules! break_unwrap { + ($e:expr) => { + match $e { + Some(t) => *t, + _ => break, + } + }; +} + +impl DHCPOption { + pub fn from_bytes(bytes: &[u8]) -> Vec { + let mut options: Vec = Vec::new(); + + let mut iterator = bytes.iter(); + loop { + match break_unwrap!(iterator.next()) { + 0 => continue, + 255 => break, + 1 => { + break_unwrap!(iterator.next()); + + let (m1, m2, m3, m4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::SubnetMask(Ipv4Addr::new(m1, m2, m3, m4))); + } + + 2 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::TimeOffset(total)); + } + + 3 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::Router(addresses)); + } + + 4 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::TimeServer(addresses)); + } + + 5 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NameServer(addresses)); + } + + 6 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::DomainNameServer(addresses)); + } + + 7 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::LogServer(addresses)); + } + + 8 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::CookieServer(addresses)); + } + + 9 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::LPRServer(addresses)); + } + + 10 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::ImpressServer(addresses)); + } + + 11 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::ResourceLocationServer(addresses)); + } + + 12 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::HostName(s.to_owned())), + Err(_) => (), + } + } + + 13 => { + break_unwrap!(iterator.next()); + let mut total: u16 = 0; + for _ in 0..2 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u16; + } + + options.push(DHCPOption::BootfileSize(total)); + } + + 14 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::MeritDumpFile(s.to_owned())), + Err(_) => (), + } + } + + 15 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::DomainName(s.to_owned())), + Err(_) => (), + } + } + + 16 => { + break_unwrap!(iterator.next()); + + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::SwapServer(Ipv4Addr::new(a1, a2, a3, a4))); + } + + 17 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::RootPath(s.to_owned())), + Err(_) => (), + } + } + + 18 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::ExtensionsPath(s.to_owned())), + Err(_) => (), + } + } + + 19 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::IPForwarding( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 20 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::NonLocalSourceRouting( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 21 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec<(Ipv4Addr, Ipv4Addr)> = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + let (m1, m2, m3, m4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses + .push((Ipv4Addr::new(a1, a2, a3, a4), Ipv4Addr::new(m1, m2, m3, m4))); + } + + options.push(DHCPOption::PolicyFilter(addresses)); + } + + 22 => { + break_unwrap!(iterator.next()); + let mut total: u16 = 0; + for _ in 0..2 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u16; + } + + options.push(DHCPOption::MaximumDatagramReassemblySize(total)); + } + + 23 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::DefaultIPTTL(break_unwrap!(iterator.next()))); + } + + 24 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::PathMTUAgingTimeout(total)); + } + + 25 => { + let count = break_unwrap!(iterator.next()) / 2; + let mut plateaus: Vec = Vec::new(); + + for _ in 0..count { + let mut total: u16 = 0; + for _ in 0..2 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u16; + } + plateaus.push(total); + } + + options.push(DHCPOption::PathMTUPlateaus(plateaus)); + } + + 26 => { + break_unwrap!(iterator.next()); + let mut total: u16 = 0; + for _ in 0..2 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u16; + } + + options.push(DHCPOption::InterfaceMTU(total)); + } + + 27 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::AllSubnetsLocal( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 28 => { + break_unwrap!(iterator.next()); + + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::BroadcastAddress(Ipv4Addr::new(a1, a2, a3, a4))); + } + + 29 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::PerformMaskDiscovery( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 30 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::MaskSupplier( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 31 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::PerformRouterDiscovery( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 32 => { + break_unwrap!(iterator.next()); + + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::RouterSolicitationAddress(Ipv4Addr::new( + a1, a2, a3, a4, + ))); + } + + 33 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec<(Ipv4Addr, Ipv4Addr)> = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + let (r1, r2, r3, r4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses + .push((Ipv4Addr::new(a1, a2, a3, a4), Ipv4Addr::new(r1, r2, r3, r4))); + } + + options.push(DHCPOption::StaticRoutes(addresses)); + } + + 34 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::TrailerEncapsultion( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 35 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::ARPCacheTimeout(total)); + } + + 37 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::EthernetEncapsulation( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 38 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::DefaultIPTTL(break_unwrap!(iterator.next()))); + } + + 39 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::TCPKeepaliveGarbage( + if break_unwrap!(iterator.next()) == 0 { + false + } else { + true + }, + )); + } + + 40 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::NISDomain(s.to_owned())), + Err(_) => (), + } + } + + 41 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NetworkInformationServers(addresses)); + } + + 42 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NTPServers(addresses)); + } + + 43 => { + let count = break_unwrap!(iterator.next()); + let mut vendor_information: Vec = Vec::new(); + + for _ in 0..count { + vendor_information.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::VendorSpecificInformation(vendor_information)); + } + + 44 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NetBIOSoverTCPIPNameServer(addresses)); + } + + 45 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NetBIOSoverTCPIPDatagramDistributionServer( + addresses, + )); + } + + 46 => { + break_unwrap!(iterator.next()); + + let node_type: NetBIOSoverTCPIPNodeType = + match break_unwrap!(iterator.next()).try_into() { + Ok(n) => n, + _ => continue, + }; + + options.push(DHCPOption::NetBIOSoverTCPIPNodeType(node_type)); + } + + 47 => { + let count = break_unwrap!(iterator.next()); + let mut bytes: Vec = Vec::new(); + + for _ in 0..count { + bytes.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::NetBIOSoverTCPIPScope(bytes)); + } + + 48 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::XWindowSystemFontServer(addresses)); + } + + 49 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::XWindowSystemDisplayManager(addresses)); + } + + 50 => { + break_unwrap!(iterator.next()); + + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::RequestIPAddress(Ipv4Addr::new(a1, a2, a3, a4))); + } + + 51 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::IPAddressLeaseTime(total)); + } + + 52 => { + break_unwrap!(iterator.next()); + + options.push(DHCPOption::OptionOverload( + match break_unwrap!(iterator.next()) { + 1 => (true, false), + 2 => (false, true), + 3 => (true, true), + _ => continue, + }, + )); + } + + 53 => { + break_unwrap!(iterator.next()); + + let node_type: DHCPMessageType = match break_unwrap!(iterator.next()).try_into() + { + Ok(n) => n, + _ => continue, + }; + + options.push(DHCPOption::DHCPMessageType(node_type)); + } + + 54 => { + break_unwrap!(iterator.next()); + + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + options.push(DHCPOption::ServerIdentifier(Ipv4Addr::new(a1, a2, a3, a4))); + } + + 55 => { + let count = break_unwrap!(iterator.next()); + let mut bytes: Vec = Vec::new(); + + for _ in 0..count { + bytes.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::ParameterRequest(bytes)); + } + + 56 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::Message(s.to_owned())), + Err(_) => (), + } + } + + 57 => { + break_unwrap!(iterator.next()); + let mut total: u16 = 0; + for _ in 0..2 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u16; + } + + options.push(DHCPOption::MaximumDHCPMessageSize(total)); + } + + 58 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::RenewalTime(total)); + } + + 59 => { + break_unwrap!(iterator.next()); + let mut total: u32 = 0; + for _ in 0..4 { + total <<= 8; + total += break_unwrap!(iterator.next()) as u32; + } + + options.push(DHCPOption::RebindingTime(total)); + } + + 60 => { + let count = break_unwrap!(iterator.next()); + let mut bytes: Vec = Vec::new(); + + for _ in 0..count { + bytes.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::ClassIdentifier(bytes)); + } + + 61 => { + let count = break_unwrap!(iterator.next()); + let mut bytes: Vec = Vec::new(); + + for _ in 0..count { + bytes.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::ClientIdentifier(bytes)); + } + + 64 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::NISPlusDomain(s.to_owned())), + Err(_) => (), + } + } + + 65 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NISPlusServers(addresses)); + } + + 66 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::TFTPServerName(s.to_owned())), + Err(_) => (), + } + } + + 67 => { + let count = break_unwrap!(iterator.next()); + let mut chars: Vec = Vec::new(); + + for _ in 0..count { + chars.push(break_unwrap!(iterator.next())); + } + + // If data is invalid just silently fail and act as if the option didn't exist. + match std::str::from_utf8(&chars) { + Ok(s) => options.push(DHCPOption::BootfileName(s.to_owned())), + Err(_) => (), + } + } + + 68 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::MobileIPHomeAgent(addresses)); + } + + 69 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::SMTPServer(addresses)); + } + + 70 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::POP3Server(addresses)); + } + + 71 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::NNTPServer(addresses)); + } + + 72 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::DefaultWWWServer(addresses)); + } + + 73 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::DefaultFingerServer(addresses)); + } + + 74 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::DefaultIRCServer(addresses)); + } + + 75 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::StreetTalkServer(addresses)); + } + + 76 => { + let count = break_unwrap!(iterator.next()) / 4; + let mut addresses: Vec = Vec::new(); + + for _ in 0..count { + let (a1, a2, a3, a4) = ( + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + break_unwrap!(iterator.next()), + ); + addresses.push(Ipv4Addr::new(a1, a2, a3, a4)); + } + + options.push(DHCPOption::STDAServer(addresses)); + } + + // Catchall for if we cannot decode the option to a specific enum variant. + n => { + let count = break_unwrap!(iterator.next()); + + let mut bytes: Vec = Vec::new(); + + for _ in 0..count { + bytes.push(break_unwrap!(iterator.next())); + } + + options.push(DHCPOption::Option(n, bytes)); + } + } + } + + return options; + } + + pub fn to_bytes(options: Vec) -> Vec { + let mut bytes: Vec = Vec::new(); + bytes.push(0x63); + bytes.push(0x82); + bytes.push(0x53); + bytes.push(0x63); + + for option in options { + match option { + DHCPOption::Pad => bytes.push(0), + DHCPOption::End => bytes.push(255), + + DHCPOption::SubnetMask(addr) => { + bytes.push(1); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::TimeOffset(x) => { + bytes.push(2); + bytes.push(4); + bytes.extend_from_slice(&x.to_be_bytes()); + } + + DHCPOption::Router(addrs) => { + bytes.push(3); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::TimeServer(addrs) => { + bytes.push(4); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::NameServer(addrs) => { + bytes.push(5); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::DomainNameServer(addrs) => { + bytes.push(6); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::LogServer(addrs) => { + bytes.push(7); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::CookieServer(addrs) => { + bytes.push(8); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::LPRServer(addrs) => { + bytes.push(9); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::ImpressServer(addrs) => { + bytes.push(10); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::ResourceLocationServer(addrs) => { + bytes.push(11); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::HostName(s) => { + bytes.push(12); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::BootfileSize(l) => { + bytes.push(13); + bytes.push(2); + bytes.extend_from_slice(&(l as u16).to_be_bytes()); + } + + DHCPOption::MeritDumpFile(s) => { + bytes.push(14); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::DomainName(s) => { + bytes.push(15); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::SwapServer(addr) => { + bytes.push(16); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::RootPath(s) => { + bytes.push(17); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::ExtensionsPath(s) => { + bytes.push(18); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::IPForwarding(b) => { + bytes.push(19); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::NonLocalSourceRouting(b) => { + bytes.push(20); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::PolicyFilter(addrs) => { + bytes.push(21); + let count = addrs.len() * 8; + bytes.push(count as u8); + for (addr, mask) in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + bytes.extend_from_slice(&(u32::from(mask)).to_be_bytes()); + } + } + + DHCPOption::MaximumDatagramReassemblySize(l) => { + bytes.push(22); + bytes.push(2); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::DefaultIPTTL(l) => { + bytes.push(23); + bytes.push(1); + bytes.push(l); + } + + DHCPOption::PathMTUAgingTimeout(l) => { + bytes.push(24); + bytes.push(2); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::PathMTUPlateaus(plateaus) => { + bytes.push(25); + bytes.push((plateaus.len() * 2) as u8); + for plateau in plateaus { + bytes.extend_from_slice(&plateau.to_be_bytes()); + } + } + + DHCPOption::InterfaceMTU(l) => { + bytes.push(26); + bytes.push(2); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::AllSubnetsLocal(b) => { + bytes.push(27); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::BroadcastAddress(addr) => { + bytes.push(28); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::PerformMaskDiscovery(b) => { + bytes.push(29); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::MaskSupplier(b) => { + bytes.push(30); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::PerformRouterDiscovery(b) => { + bytes.push(31); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::RouterSolicitationAddress(addr) => { + bytes.push(32); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::StaticRoutes(addrs) => { + bytes.push(33); + let count = addrs.len() * 8; + bytes.push(count as u8); + for (addr, router) in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + bytes.extend_from_slice(&(u32::from(router)).to_be_bytes()); + } + } + + DHCPOption::TrailerEncapsultion(b) => { + bytes.push(34); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::ARPCacheTimeout(l) => { + bytes.push(35); + bytes.push(4); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::EthernetEncapsulation(b) => { + bytes.push(36); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::TCPDefaultTTL(l) => { + bytes.push(37); + bytes.push(1); + bytes.push(l); + } + + DHCPOption::TCPKeepaliveInterval(l) => { + bytes.push(38); + bytes.push(4); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::TCPKeepaliveGarbage(b) => { + bytes.push(39); + bytes.push(1); + bytes.push(b as u8); + } + + DHCPOption::NISDomain(s) => { + bytes.push(40); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::NetworkInformationServers(addrs) => { + bytes.push(41); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::NTPServers(addrs) => { + bytes.push(42); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::VendorSpecificInformation(b) => { + bytes.push(43); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + + DHCPOption::NetBIOSoverTCPIPNameServer(addrs) => { + bytes.push(44); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::NetBIOSoverTCPIPDatagramDistributionServer(addrs) => { + bytes.push(45); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::NetBIOSoverTCPIPNodeType(n) => { + bytes.push(46); + bytes.push(1); + bytes.push(n.into()); + } + + DHCPOption::NetBIOSoverTCPIPScope(b) => { + bytes.push(47); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + + DHCPOption::XWindowSystemFontServer(addrs) => { + bytes.push(48); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::XWindowSystemDisplayManager(addrs) => { + bytes.push(49); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::RequestIPAddress(addr) => { + bytes.push(50); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::IPAddressLeaseTime(l) => { + bytes.push(51); + bytes.push(4); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::OptionOverload(b) => { + let v = match b { + (true, false) => 1, + (false, true) => 2, + (true, true) => 3, + _ => continue, + }; + + bytes.push(52); + bytes.push(1); + bytes.push(v); + } + + DHCPOption::DHCPMessageType(t) => { + bytes.push(53); + bytes.push(1); + bytes.push(t.into()); + } + + DHCPOption::ServerIdentifier(addr) => { + bytes.push(54); + bytes.push(4); + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + + DHCPOption::ParameterRequest(b) => { + bytes.push(55); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + + DHCPOption::Message(s) => { + bytes.push(56); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::MaximumDHCPMessageSize(l) => { + bytes.push(57); + bytes.push(2); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::RenewalTime(l) => { + bytes.push(58); + bytes.push(4); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::RebindingTime(l) => { + bytes.push(59); + bytes.push(4); + bytes.extend_from_slice(&l.to_be_bytes()); + } + + DHCPOption::ClassIdentifier(b) => { + bytes.push(60); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + + DHCPOption::ClientIdentifier(b) => { + bytes.push(61); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + + DHCPOption::NISPlusDomain(s) => { + bytes.push(64); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::NISPlusServers(addrs) => { + bytes.push(65); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::TFTPServerName(s) => { + bytes.push(66); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::BootfileName(s) => { + bytes.push(67); + bytes.push(s.len() as u8); + bytes.extend_from_slice(s.as_bytes()); + } + + DHCPOption::MobileIPHomeAgent(addrs) => { + bytes.push(68); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::SMTPServer(addrs) => { + bytes.push(69); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::POP3Server(addrs) => { + bytes.push(70); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::NNTPServer(addrs) => { + bytes.push(71); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::DefaultWWWServer(addrs) => { + bytes.push(72); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::DefaultFingerServer(addrs) => { + bytes.push(73); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::DefaultIRCServer(addrs) => { + bytes.push(74); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::StreetTalkServer(addrs) => { + bytes.push(75); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::STDAServer(addrs) => { + bytes.push(76); + let count = addrs.len() * 4; + bytes.push(count as u8); + for addr in addrs { + bytes.extend_from_slice(&(u32::from(addr)).to_be_bytes()); + } + } + + DHCPOption::Option(n, b) => { + bytes.push(n); + bytes.push(b.len() as u8); + bytes.extend_from_slice(&b); + } + } + } + + return bytes; + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..520433b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +//! DHCP/BOOTP packet encode/decode library. +//! +//! The purpose of this library is to provide an easy to use interface over +//! DHCP, BOOTP and UDP packets to enable the creation of programs that +//! make use of DHCP. +//! +//! BOOTP specific functionality is provided by the `dhcprs::bootp` module +//! DHCP specific functionality is provided by the `dhcprs::dhcp` module +//! +//! There is additionally simple UDP packet encode/decode provided by the +//! `dhcprs::udpbuilder`. + +pub mod bootp; +pub mod dhcp; +pub mod udpbuilder; diff --git a/src/udpbuilder.rs b/src/udpbuilder.rs new file mode 100644 index 0000000..ee31244 --- /dev/null +++ b/src/udpbuilder.rs @@ -0,0 +1,175 @@ +#[allow(dead_code)] +#[repr(packed)] +pub struct RawUDPPacket { + // IP Header + version_ihl: u8, // First 4 bits: Version (always 4), next 4 bits: Internet Header Length (Header size in u32s) + dscp_ecn: u8, // Differentiated Services Code Point / Explicit Congestion Notification, zero for our purposes + total_length: u16, // Total length of the IP header + UDP header + data + identification: u16, // Identification (for fragmentation purposes) + flags_fragment_offset: u16, // Flags and the fragment offset (0x4000 for DF) + ttl: u8, // Time to live + protocol: u8, // Proto (UDP is 0x11 / dec 17) + ip_checksum: u16, // Ones' complement of the ones' complement sum of all 16 bit words of the header + source_addr: u32, // Source address + dest_addr: u32, // Destination address + + // UDP Header + source_port: u16, // Source port + dest_port: u16, // Destination port + length: u16, // Length of UDP Header + data + udp_checksum: u16, // Ones' complement of the ones' complement sum of all 16 bit words of the pseudoheader + udp header + data + + // UDP Data + data: [u8; 546], // 546 byte array to store a RawBOOTPPacket +} + +impl RawUDPPacket { + pub fn as_bytes(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, 574) } + } +} + +impl From for RawUDPPacket { + fn from(item: UDPPacket) -> Self { + let ph: PseudoHeader = item.clone().into(); + let udp_checksum = ph.checksum(); + + let source_addr: u32 = item.source_addr.into(); + let dest_addr: u32 = item.dest_addr.into(); + + let mut packet = Self { + version_ihl: 0x45, // Version 4, IHL of 5 (20 bytes) + dscp_ecn: 0, // No need for either DSCP or ECN + total_length: 574_u16.to_be(), // Will always be 576 bytes + identification: 0x1337, // No need (no fragmentation) + flags_fragment_offset: 0, + ttl: 64, // Set TTL to 255 because why not + protocol: 17, // UDP + ip_checksum: 0, // Zero for now, will be set later + source_addr: source_addr.to_be(), + dest_addr: dest_addr.to_be(), + source_port: item.source_port.to_be(), + dest_port: item.dest_port.to_be(), + length: ((item.data.len() + 8) as u16).to_be(), + udp_checksum: udp_checksum, + data: item.data, + }; + + let header_u16 = + unsafe { std::slice::from_raw_parts(&packet as *const Self as *const u16, 10) }; + let mut total: u16 = 0; + for &word in header_u16 { + let (value, carry) = total.overflowing_add(word); + if carry { + total = value + 1; + } else { + total = value; + } + } + + packet.ip_checksum = !total; + return packet; + } +} + +#[allow(dead_code)] +#[repr(packed)] +struct PseudoHeader { + source_addr: u32, + dest_addr: u32, + zero: u8, + protocol: u8, + udp_length: u16, + source_port: u16, + dest_port: u16, + udp_length2: u16, + udp_checksum: u16, + data: [u8; 546], +} + +impl From for PseudoHeader { + fn from(item: UDPPacket) -> Self { + let source_addr: u32 = item.source_addr.into(); + let dest_addr: u32 = item.dest_addr.into(); + Self { + source_addr: source_addr.to_be(), + dest_addr: dest_addr.to_be(), + zero: 0, + protocol: 17, + udp_length: ((item.data.len() + 8) as u16).to_be(), + source_port: item.source_port.to_be(), + dest_port: item.dest_port.to_be(), + udp_length2: ((item.data.len() + 8) as u16).to_be(), + udp_checksum: 0, + data: item.data, + } + } +} + +impl PseudoHeader { + pub fn checksum(&self) -> u16 { + let as_u16 = unsafe { + std::slice::from_raw_parts( + self as *const Self as *const u16, + (self.data.len() + 20) / 2, + ) + }; + let mut total: u16 = 0; + for &word in as_u16 { + let (value, carry) = total.overflowing_add(word); + if carry { + total = value + 1; + } else { + total = value; + } + } + !total + } +} + +#[derive(Clone)] +pub struct UDPPacket { + pub source_addr: std::net::Ipv4Addr, + pub dest_addr: std::net::Ipv4Addr, + pub source_port: u16, + pub dest_port: u16, + data: [u8; 546], +} + +impl From for UDPPacket { + fn from(item: RawUDPPacket) -> Self { + Self { + source_addr: std::net::Ipv4Addr::from(item.source_addr.to_be_bytes()), + dest_addr: std::net::Ipv4Addr::from(item.source_addr.to_be_bytes()), + source_port: u16::from_be(item.source_port), + dest_port: u16::from_be(item.dest_port), + data: item.data, + } + } +} + +impl UDPPacket { + /// Creates a new UDPPacket + /// + /// Should be converted to a `dhcprs::udpbuilder::RawUDPPacket` to send. + pub fn new( + source_addr: std::net::Ipv4Addr, + dest_addr: std::net::Ipv4Addr, + source_port: u16, + dest_port: u16, + data: [u8; 546], + ) -> Self { + Self { + source_addr: source_addr, + dest_addr: dest_addr, + source_port: source_port, + dest_port: dest_port, + data: data, + } + } + + /// Returns a reference to the payload of the UDP packet. + pub fn get_data(&self) -> &[u8] { + return &self.data; + } +}