97 lines
3.0 KiB
Rust
97 lines
3.0 KiB
Rust
use anyhow::Result;
|
|
use std::path::PathBuf;
|
|
use windows::{
|
|
Win32::{
|
|
NetworkManagement::WindowsFirewall::{
|
|
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_ANY,
|
|
NET_FW_IP_PROTOCOL_TCP, NET_FW_IP_PROTOCOL_UDP, NET_FW_RULE_DIR_IN,
|
|
NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
|
},
|
|
System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance},
|
|
},
|
|
core::BSTR,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Firewall {
|
|
policy: INetFwPolicy2,
|
|
}
|
|
|
|
impl Default for Firewall {
|
|
fn default() -> Self {
|
|
Self {
|
|
policy: unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Firewall {
|
|
pub fn add(
|
|
&self,
|
|
name: &str,
|
|
mode: RuleMode,
|
|
direction: RuleDirection,
|
|
protocol: RuleProtocol,
|
|
) -> Result<()> {
|
|
let add_rule = || {
|
|
let rules = unsafe { self.policy.Rules() }?;
|
|
unsafe { rules.Remove(&BSTR::from(name)) }?;
|
|
let rule: INetFwRule =
|
|
unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }?;
|
|
unsafe { rule.SetName(&BSTR::from(name)) }?;
|
|
match mode {
|
|
RuleMode::Executable(exe) => {
|
|
unsafe { rule.SetApplicationName(&BSTR::from(exe.to_string_lossy().as_ref())) }?
|
|
}
|
|
RuleMode::Address(ip) => unsafe { rule.SetRemoteAddresses(&BSTR::from(ip)) }?,
|
|
}
|
|
match direction {
|
|
RuleDirection::In => unsafe { rule.SetDirection(NET_FW_RULE_DIR_IN) }?,
|
|
RuleDirection::Out => unsafe { rule.SetDirection(NET_FW_RULE_DIR_OUT) }?,
|
|
}
|
|
unsafe { rule.SetEnabled(true.into()) }?;
|
|
unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }?;
|
|
match protocol {
|
|
RuleProtocol::Any => unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }?,
|
|
RuleProtocol::Tcp => unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_TCP.0) }?,
|
|
RuleProtocol::Udp => unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_UDP.0) }?,
|
|
}
|
|
unsafe { rules.Add(&rule) }?;
|
|
Ok(())
|
|
};
|
|
add_rule().inspect_err(|e| log::warn!("Failed to add rule '{name}': {e}"))
|
|
}
|
|
|
|
pub fn remove(&self, name: &str) -> Result<()> {
|
|
let remove_rule = || {
|
|
let rules = unsafe { self.policy.Rules() }?;
|
|
unsafe { rules.Remove(&BSTR::from(name)) }?;
|
|
Ok(())
|
|
};
|
|
remove_rule().inspect_err(|e| log::warn!("Failed to remove rule '{name}': {e}"))
|
|
}
|
|
|
|
pub fn is_blocked(&self, name: &str) -> Result<bool> {
|
|
let rules = unsafe { self.policy.Rules() }?;
|
|
let rule_exists = unsafe { rules.Item(&BSTR::from(name)) }.is_ok();
|
|
Ok(rule_exists)
|
|
}
|
|
}
|
|
|
|
pub enum RuleMode {
|
|
Executable(PathBuf),
|
|
Address(String),
|
|
}
|
|
|
|
pub enum RuleDirection {
|
|
In,
|
|
Out,
|
|
}
|
|
|
|
pub enum RuleProtocol {
|
|
Any,
|
|
#[allow(unused)]
|
|
Tcp,
|
|
Udp,
|
|
}
|