#![allow(missing_debug_implementations)]
mod config;
pub use config::*;
use crate::v4::message::*;
use mac_address::get_mac_address;
use rand::RngCore;
use rand::rngs::ThreadRng;
use std::net::Ipv4Addr;
pub const CLIENT_PORT: u16 = 68;
pub const TEST_CLIENT_PORT: u16 = 8068;
pub const RESOLVED_CLIENT_PORT: u16 = if cfg!(debug_assertions) {
TEST_CLIENT_PORT
} else {
CLIENT_PORT
};
fn get_chaddr() -> [u8; 16] {
let address = get_mac_address().unwrap().unwrap().bytes();
let mut chaddr: [u8; 16] = [0; 16];
chaddr[..6].copy_from_slice(&address[..6]);
chaddr
}
pub struct Client {
pub socket: Socket,
pub haddr: [u8; 16],
rng: Box<dyn RngCore>,
}
impl Client {
pub fn new(config: ClientConfig) -> Self {
Self {
socket: Socket::new(config.socket_config),
haddr: get_chaddr(),
rng: Box::new(ThreadRng::default()),
}
}
pub fn set_rng(&mut self, rng: Box<dyn RngCore>) {
self.rng = rng;
}
fn get_xid(&mut self) -> u32 {
self.rng.next_u32()
}
pub fn discover(&mut self) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: self.get_xid(),
secs: 0,
flags: Flags { broadcast: false }, ciaddr: Ipv4Addr::UNSPECIFIED,
yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Discover),
]
.into(),
}
}
pub fn extend(&mut self, address: Ipv4Addr) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: self.get_xid(),
secs: 0,
flags: Flags { broadcast: false },
ciaddr: address, yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Request),
]
.into(),
}
}
pub fn verify(&mut self, address: Ipv4Addr) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: self.get_xid(),
secs: 0,
flags: Flags { broadcast: false },
ciaddr: Ipv4Addr::UNSPECIFIED, yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Request),
MessageOptions::RequestedIp(AddressOption::new(address)), ]
.into(),
}
}
pub fn request(&self, message: Message) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: message.xid,
secs: 0,
flags: Flags { broadcast: false }, ciaddr: Ipv4Addr::UNSPECIFIED, yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: message.chaddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Request),
MessageOptions::RequestedIp(AddressOption::new(message.yiaddr)), MessageOptions::ServerIdentifier(AddressOption::new(message.siaddr)), ]
.into(),
}
}
pub fn decline(&self, message: Message) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: message.xid,
secs: 0,
flags: Flags { broadcast: false },
ciaddr: Ipv4Addr::UNSPECIFIED,
yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Decline),
MessageOptions::RequestedIp(AddressOption::new(message.yiaddr)), MessageOptions::ServerIdentifier(AddressOption::new(message.siaddr)), ]
.into(),
}
}
pub fn release(&mut self) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: self.get_xid(),
secs: 0,
flags: Flags { broadcast: false },
ciaddr: Ipv4Addr::UNSPECIFIED, yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Release),
MessageOptions::ServerIdentifier(AddressOption::new(Ipv4Addr::UNSPECIFIED)), ]
.into(),
}
}
pub fn inform(&mut self, parameter_list: Vec<u8>) -> Message {
Message {
op: Ops::Request,
htype: HTypes::Ethernet,
hlen: 6,
hops: 0,
xid: self.get_xid(),
secs: 0,
flags: Flags { broadcast: false },
ciaddr: Ipv4Addr::UNSPECIFIED, yiaddr: Ipv4Addr::UNSPECIFIED,
siaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
chaddr: self.haddr,
sname: SName::EMPTY,
file: File::EMPTY,
magic: MAGIC,
options: vec![
MessageOptions::MessageType(MessageTypes::Inform),
MessageOptions::RequestedOptions(parameter_list),
]
.into(),
}
}
}