Initial commit

This commit is contained in:
Pascal Engélibert 2025-03-29 22:15:07 +01:00
commit b35120be22
9 changed files with 677 additions and 0 deletions

131
src/policy.rs Normal file
View file

@ -0,0 +1,131 @@
use regex::{Regex, RegexSet};
#[derive(Clone, Debug)]
pub enum Action {
Allow,
Challenge,
Drop,
}
#[derive(Clone, Debug)]
pub enum Filter {
Bool(bool),
FirstLineMatch(String),
HeaderLineMatch(String),
And(Vec<Filter>),
Or(Vec<Filter>),
Not(Box<Filter>),
}
impl Filter {
fn compile<'a>(
&'a self,
first_line_regexes: &mut Vec<&'a str>,
header_line_regexes: &mut Vec<&'a str>,
) -> CompiledFilter {
match self {
Filter::Bool(v) => CompiledFilter::Bool(*v),
Filter::And(filters) => CompiledFilter::And(
filters
.iter()
.map(|filter| filter.compile(first_line_regexes, header_line_regexes))
.collect(),
),
Filter::Or(filters) => CompiledFilter::Or(
filters
.iter()
.map(|filter| filter.compile(first_line_regexes, header_line_regexes))
.collect(),
),
Filter::Not(filter) => CompiledFilter::Not(Box::new(
filter.compile(first_line_regexes, header_line_regexes),
)),
Filter::FirstLineMatch(regex) => {
let filter = CompiledFilter::FirstLineMatch(first_line_regexes.len());
first_line_regexes.push(regex);
filter
}
Filter::HeaderLineMatch(regex) => {
let filter = CompiledFilter::HeaderLineMatch(header_line_regexes.len());
header_line_regexes.push(regex);
filter
}
}
}
}
#[derive(Clone, Debug)]
pub struct Policy {
pub name: String,
pub filter: Filter,
pub action: Action,
pub priority: i32,
}
pub enum CompiledFilter {
Bool(bool),
FirstLineMatch(usize),
HeaderLineMatch(usize),
And(Vec<CompiledFilter>),
Or(Vec<CompiledFilter>),
Not(Box<CompiledFilter>),
}
pub struct CompiledPolicy {
pub name: String,
pub filter: CompiledFilter,
pub priority: i32,
pub action: Action,
}
pub struct CompiledPolicies {
pub first_line_regex_set: Option<RegexSet>,
pub header_line_regex_set: Option<RegexSet>,
pub policies: Vec<CompiledPolicy>,
}
impl CompiledPolicies {
pub fn new<'a>(policies: impl IntoIterator<Item = &'a Policy>) -> Self {
let mut first_line_regexes = Vec::new();
let mut header_line_regexes = Vec::new();
let mut compiled_policies = Vec::new();
for policy in policies {
let compiled_policy = CompiledPolicy {
name: policy.name.clone(),
filter: policy
.filter
.compile(&mut first_line_regexes, &mut header_line_regexes),
priority: policy.priority,
action: policy.action.clone(),
};
compiled_policies.push(compiled_policy);
}
CompiledPolicies {
first_line_regex_set: if first_line_regexes.is_empty() {None} else {Some(RegexSet::new(&first_line_regexes).unwrap())},
header_line_regex_set: if header_line_regexes.is_empty() {None} else {Some(RegexSet::new(&header_line_regexes).unwrap())},
policies: compiled_policies,
}
}
pub fn evaluate<'a>(&self, mut header_lines: impl Iterator<Item=&'a [u8]>) -> Result<Option<&CompiledPolicy>, PolicyEvaluationError> {
let mut best_policy = None;
let mut best_priority = i32::MAX;
let first_line = header_lines.next().ok_or(PolicyEvaluationError::NoFirstLine)?;
if let Some(first_line_regex_set) = &self.first_line_regex_set {
//let matches = first_line_regex_set.matches(first_line);
}
Ok(best_policy)
}
}
#[derive(Debug)]
pub enum PolicyEvaluationError {
/// First HTTP line is too long or absent
NoFirstLine,
}