use std::net::Ipv4Addr;
use argh::FromArgs;
use dhcproto::v4;
use mac_address::MacAddress;
use crate::opts::{self, parse_mac, parse_opts, parse_params};
#[derive(FromArgs, PartialEq, Debug, Clone)]
#[argh(subcommand, name = "release")]
pub struct ReleaseArgs {
#[argh(
option,
short = 'c',
from_str_fn(parse_mac),
default = "opts::get_mac()"
)]
pub chaddr: MacAddress,
#[argh(option, short = 'g', default = "Ipv4Addr::UNSPECIFIED")]
pub giaddr: Ipv4Addr,
#[argh(option, default = "Ipv4Addr::UNSPECIFIED")]
pub ciaddr: Ipv4Addr,
#[argh(option, short = 'y', default = "Ipv4Addr::UNSPECIFIED")]
pub yiaddr: Ipv4Addr,
#[argh(option, short = 's')]
pub sident: Option<Ipv4Addr>,
#[argh(option)]
pub subnet_select: Option<Ipv4Addr>,
#[argh(option)]
pub relay_link: Option<Ipv4Addr>,
#[argh(option, short = 'o', from_str_fn(parse_opts))]
pub opt: Vec<v4::DhcpOption>,
#[argh(option, from_str_fn(parse_params), default = "opts::default_params()")]
pub params: Vec<v4::OptionCode>,
}
impl Default for ReleaseArgs {
fn default() -> Self {
Self {
chaddr: opts::get_mac(),
ciaddr: Ipv4Addr::UNSPECIFIED,
giaddr: Ipv4Addr::UNSPECIFIED,
yiaddr: Ipv4Addr::UNSPECIFIED,
sident: None,
subnet_select: None,
relay_link: None,
opt: Vec::new(),
params: opts::default_params(),
}
}
}
impl ReleaseArgs {
pub fn build(&self) -> v4::Message {
let mut msg = v4::Message::new(
self.ciaddr,
self.yiaddr,
Ipv4Addr::UNSPECIFIED,
self.giaddr,
&self.chaddr.bytes(),
);
msg.opts_mut()
.insert(v4::DhcpOption::MessageType(v4::MessageType::Release));
msg.opts_mut().insert(v4::DhcpOption::ClientIdentifier(
self.chaddr.bytes().to_vec(),
));
msg.opts_mut()
.insert(v4::DhcpOption::ParameterRequestList(self.params.clone()));
for opt in &self.opt {
msg.opts_mut().insert(opt.clone());
}
if let Some(ip) = self.sident {
msg.opts_mut().insert(v4::DhcpOption::ServerIdentifier(ip));
}
if let Some(ip) = self.relay_link {
let mut info = v4::relay::RelayAgentInformation::default();
info.insert(v4::relay::RelayInfo::LinkSelection(ip));
msg.opts_mut()
.insert(v4::DhcpOption::RelayAgentInformation(info));
}
if let Some(ip) = self.subnet_select {
msg.opts_mut().insert(v4::DhcpOption::SubnetSelection(ip));
}
msg
}
}
#[cfg(feature = "script")]
use rhai::{plugin::*, EvalAltResult};
#[cfg(feature = "script")]
#[export_module]
pub mod release_mod {
use tracing::trace;
#[rhai_fn()]
pub fn args_default() -> ReleaseArgs {
ReleaseArgs::default()
}
#[rhai_fn(global, name = "to_string", name = "to_debug", pure)]
pub fn to_string(args: &mut ReleaseArgs) -> String {
format!("{:?}", args)
}
#[rhai_fn(global, get = "ciaddr", pure)]
pub fn get_ciaddr(args: &mut ReleaseArgs) -> String {
args.ciaddr.to_string()
}
#[rhai_fn(global, set = "ciaddr")]
pub fn set_ciaddr(args: &mut ReleaseArgs, ciaddr: &str) {
trace!(?ciaddr, "setting ciaddr");
args.ciaddr = ciaddr.parse::<Ipv4Addr>().expect("failed to parse ciaddr");
}
#[rhai_fn(global, get = "yiaddr", pure)]
pub fn get_yiaddr(args: &mut ReleaseArgs) -> String {
args.yiaddr.to_string()
}
#[rhai_fn(global, set = "yiaddr")]
pub fn set_yiaddr(args: &mut ReleaseArgs, yiaddr: &str) {
trace!(?yiaddr, "setting yiaddr");
args.yiaddr = yiaddr.parse::<Ipv4Addr>().expect("failed to parse ciaddr");
}
#[rhai_fn(global, get = "giaddr", pure)]
pub fn get_giaddr(args: &mut ReleaseArgs) -> String {
args.giaddr.to_string()
}
#[rhai_fn(global, set = "giaddr")]
pub fn set_giaddr(args: &mut ReleaseArgs, giaddr: &str) {
trace!(?giaddr, "setting giaddr");
args.giaddr = giaddr.parse::<Ipv4Addr>().expect("failed to parse giaddr");
}
#[rhai_fn(global, get = "relay_link", pure)]
pub fn get_relay_link(args: &mut ReleaseArgs) -> Option<String> {
args.relay_link.map(|r| r.to_string())
}
#[rhai_fn(global, set = "relay_link")]
pub fn set_relay_link(args: &mut ReleaseArgs, relay_link: &str) {
trace!(?relay_link, "setting relay_link");
args.relay_link = Some(
relay_link
.parse::<Ipv4Addr>()
.expect("failed to parse relay_link"),
);
}
#[rhai_fn(global, get = "chaddr", pure)]
pub fn get_chaddr(args: &mut ReleaseArgs) -> rhai::Blob {
args.chaddr.bytes().to_vec()
}
#[rhai_fn(global, set = "chaddr")]
pub fn set_chaddr(args: &mut ReleaseArgs, chaddr: rhai::Blob) {
trace!(?chaddr, "setting chaddr");
let bytes: [u8; 6] = chaddr.try_into().expect("failed to convert macaddress");
args.chaddr = bytes.into();
}
#[rhai_fn(global, name = "rand_chaddr")]
pub fn rand_chaddr(args: &mut ReleaseArgs) {
let chaddr = rand::random::<[u8; 6]>().into();
trace!(?chaddr, "setting random chaddr");
args.chaddr = chaddr;
}
#[rhai_fn(global, get = "sident", pure)]
pub fn get_sident(args: &mut ReleaseArgs) -> Option<String> {
args.sident.map(|r| r.to_string())
}
#[rhai_fn(global, set = "sident")]
pub fn set_sident(args: &mut ReleaseArgs, sident: &str) {
trace!(?sident, "setting req_addr");
args.sident = Some(sident.parse::<Ipv4Addr>().expect("failed to parse sident"));
}
#[rhai_fn(global, set = "opt")]
pub fn set_opt(args: &mut ReleaseArgs, opt: String) {
trace!(?opt, "adding opt to message");
args.opt
.push(crate::opts::parse_opts(&opt).expect("failed to parse opt"));
}
#[rhai_fn(global, get = "params")]
pub fn get_params(args: &mut ReleaseArgs) -> String {
crate::opts::params_to_str(&args.params)
}
#[rhai_fn(global, set = "params")]
pub fn set_params(args: &mut ReleaseArgs, params: String) {
trace!(?params, "setting params");
args.params = crate::opts::parse_params(¶ms).expect("failed to parse params");
}
}