use std::ffi::OsStr;
use std::iter;
use std::os::windows::ffi::OsStrExt;
use std::sync::Arc;
use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::{
FWP_BYTE_BLOB, FWP_BYTE_BLOB_TYPE, FWP_MATCH_EQUAL, FWP_MATCH_GREATER,
FWP_MATCH_GREATER_OR_EQUAL, FWP_MATCH_LESS, FWP_MATCH_LESS_OR_EQUAL, FWP_MATCH_RANGE,
FWP_UINT8, FWP_UINT16, FWP_UINT32, FWP_UNICODE_STRING_TYPE, FWPM_CONDITION_ALE_APP_ID,
FWPM_CONDITION_IP_LOCAL_ADDRESS, FWPM_CONDITION_IP_LOCAL_PORT, FWPM_CONDITION_IP_PROTOCOL,
FWPM_CONDITION_IP_REMOTE_ADDRESS, FWPM_CONDITION_IP_REMOTE_PORT, FWPM_FILTER_CONDITION0,
};
use windows_sys::core::GUID;
#[derive(Clone)]
pub struct PortConditionBuilder<Value> {
builder: ConditionBuilder,
_pd: std::marker::PhantomData<Value>,
}
#[doc(hidden)]
pub struct PortConditionBuilderMissingValue;
#[doc(hidden)]
pub struct PortConditionBuilderHasValue;
impl PortConditionBuilder<PortConditionBuilderMissingValue> {
pub fn remote() -> Self {
Self {
builder: ConditionBuilder::default().field(ConditionField::RemotePort),
_pd: std::marker::PhantomData,
}
}
pub fn local() -> Self {
Self {
builder: ConditionBuilder::default().field(ConditionField::LocalPort),
_pd: std::marker::PhantomData,
}
}
}
impl<Value> PortConditionBuilder<Value> {
pub fn equal(self, port: u16) -> PortConditionBuilder<PortConditionBuilderHasValue> {
PortConditionBuilder {
builder: self.builder.match_type(MatchType::Equal).value_u16(port),
_pd: std::marker::PhantomData,
}
}
}
impl PortConditionBuilder<PortConditionBuilderHasValue> {
pub fn build(self) -> Condition {
self.builder.build().expect("condition should be valid")
}
}
#[derive(Clone)]
pub struct ProtocolConditionBuilder {
builder: ConditionBuilder,
}
impl ProtocolConditionBuilder {
pub fn tcp() -> Self {
Self::new().equal(6)
}
pub fn udp() -> Self {
Self::new().equal(17)
}
pub fn icmp() -> Self {
Self::new().equal(1)
}
fn new() -> Self {
Self {
builder: ConditionBuilder::default().field(ConditionField::Protocol),
}
}
fn equal(self, protocol: u8) -> Self {
Self {
builder: self.builder.match_type(MatchType::Equal).value_u8(protocol),
}
}
pub fn build(self) -> Condition {
self.builder.build().expect("all values are set")
}
}
impl Default for ProtocolConditionBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct AppIdConditionBuilder<Value> {
builder: ConditionBuilder,
_pd: std::marker::PhantomData<Value>,
}
#[doc(hidden)]
pub struct AppIdConditionBuilderMissingValue;
#[doc(hidden)]
pub struct AppIdConditionBuilderHasValue;
impl AppIdConditionBuilder<AppIdConditionBuilderMissingValue> {
pub fn new() -> Self {
Self {
builder: ConditionBuilder::default().field(ConditionField::AppId),
_pd: std::marker::PhantomData,
}
}
}
impl<Value> AppIdConditionBuilder<Value> {
pub fn equal(
self,
app_path: impl AsRef<OsStr>,
) -> AppIdConditionBuilder<AppIdConditionBuilderHasValue> {
AppIdConditionBuilder {
builder: self
.builder
.match_type(MatchType::Equal)
.value_string(app_path),
_pd: std::marker::PhantomData,
}
}
}
impl AppIdConditionBuilder<AppIdConditionBuilderHasValue> {
pub fn build(self) -> Condition {
self.builder.build().expect("condition should be valid")
}
}
impl Default for AppIdConditionBuilder<AppIdConditionBuilderMissingValue> {
fn default() -> Self {
Self::new()
}
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MatchType {
Equal = FWP_MATCH_EQUAL,
Greater = FWP_MATCH_GREATER,
Less = FWP_MATCH_LESS,
GreaterOrEqual = FWP_MATCH_GREATER_OR_EQUAL,
LessOrEqual = FWP_MATCH_LESS_OR_EQUAL,
Range = FWP_MATCH_RANGE,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConditionField {
RemoteAddress,
LocalAddress,
RemotePort,
LocalPort,
Protocol,
AppId,
}
impl ConditionField {
pub fn guid(&self) -> &GUID {
match self {
Self::RemoteAddress => &FWPM_CONDITION_IP_REMOTE_ADDRESS,
Self::LocalAddress => &FWPM_CONDITION_IP_LOCAL_ADDRESS,
Self::RemotePort => &FWPM_CONDITION_IP_REMOTE_PORT,
Self::LocalPort => &FWPM_CONDITION_IP_LOCAL_PORT,
Self::Protocol => &FWPM_CONDITION_IP_PROTOCOL,
Self::AppId => &FWPM_CONDITION_ALE_APP_ID,
}
}
}
#[derive(Default, Clone)]
struct ConditionBuilder {
field: Option<ConditionField>,
match_type: Option<MatchType>,
value: Option<Arc<ConditionValue>>,
}
#[derive(Clone)]
enum ConditionValue {
UInt32(u32),
UInt16(u16),
UInt8(u8),
String(Vec<u16>),
ByteBlob { blob: FWP_BYTE_BLOB, _data: Vec<u8> },
}
impl ConditionBuilder {
pub fn field(mut self, field: ConditionField) -> Self {
self.field = Some(field);
self
}
pub fn match_type(mut self, match_type: MatchType) -> Self {
self.match_type = Some(match_type);
self
}
#[allow(dead_code)]
pub fn value_u32(mut self, value: u32) -> Self {
self.value = Some(ConditionValue::UInt32(value).into());
self
}
pub fn value_u16(mut self, value: u16) -> Self {
self.value = Some(ConditionValue::UInt16(value).into());
self
}
pub fn value_u8(mut self, value: u8) -> Self {
self.value = Some(ConditionValue::UInt8(value).into());
self
}
pub fn value_string(mut self, value: impl AsRef<OsStr>) -> Self {
let wide_string: Vec<u16> = value
.as_ref()
.encode_wide()
.chain(iter::once(0u16))
.collect();
self.value = Some(ConditionValue::String(wide_string).into());
self
}
#[allow(dead_code)]
pub fn value_byte_blob(mut self, data: &[u8]) -> Self {
let data = data.to_vec();
self.value = Some(
ConditionValue::ByteBlob {
blob: FWP_BYTE_BLOB {
data: data.as_ptr() as *mut _,
size: u32::try_from(data.len()).unwrap(),
},
_data: data,
}
.into(),
);
self
}
pub fn build(self) -> Option<Condition> {
let field = self.field?;
let match_type = self.match_type?;
let value = self.value?;
let mut raw_condition: FWPM_FILTER_CONDITION0 = unsafe { std::mem::zeroed() };
raw_condition.fieldKey = *field.guid();
raw_condition.matchType = match_type as i32;
match &*value {
ConditionValue::UInt32(val) => {
raw_condition.conditionValue.r#type = FWP_UINT32;
raw_condition.conditionValue.Anonymous.uint32 = *val;
}
ConditionValue::UInt16(val) => {
raw_condition.conditionValue.r#type = FWP_UINT16;
raw_condition.conditionValue.Anonymous.uint16 = *val;
}
ConditionValue::UInt8(val) => {
raw_condition.conditionValue.r#type = FWP_UINT8;
raw_condition.conditionValue.Anonymous.uint8 = *val;
}
ConditionValue::String(wide_str) => {
raw_condition.conditionValue.r#type = FWP_UNICODE_STRING_TYPE;
raw_condition.conditionValue.Anonymous.unicodeString = wide_str.as_ptr() as *mut _;
}
ConditionValue::ByteBlob { blob, _data: _ } => {
raw_condition.conditionValue.r#type = FWP_BYTE_BLOB_TYPE;
raw_condition.conditionValue.Anonymous.byteBlob = blob as *const _ as *mut _;
}
}
Some(Condition {
raw_condition,
_value: value,
})
}
}
#[derive(Clone)]
pub struct Condition {
raw_condition: FWPM_FILTER_CONDITION0,
_value: Arc<ConditionValue>,
}
impl Condition {
pub(crate) fn raw_condition(&self) -> &FWPM_FILTER_CONDITION0 {
&self.raw_condition
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_condition_port_remote() {
let condition = PortConditionBuilder::remote().equal(80).build();
assert_eq!(
condition.raw_condition.fieldKey.data1,
FWPM_CONDITION_IP_REMOTE_PORT.data1
);
assert_eq!(
condition.raw_condition.fieldKey.data2,
FWPM_CONDITION_IP_REMOTE_PORT.data2
);
assert_eq!(
condition.raw_condition.fieldKey.data3,
FWPM_CONDITION_IP_REMOTE_PORT.data3
);
assert_eq!(
condition.raw_condition.fieldKey.data4,
FWPM_CONDITION_IP_REMOTE_PORT.data4
);
assert_eq!(condition.raw_condition.matchType, FWP_MATCH_EQUAL);
assert_eq!(
unsafe { condition.raw_condition.conditionValue.Anonymous.uint16 },
80
);
}
}