trojan_rules/rule.rs
1//! Rule and action type definitions.
2
3use std::net::IpAddr;
4
5use ipnet::IpNet;
6use serde::{Deserialize, Serialize};
7
8/// A parsed rule from a rule-set file.
9#[derive(Debug, Clone)]
10pub enum ParsedRule {
11 Domain(String),
12 DomainSuffix(String),
13 DomainKeyword(String),
14 IpCidr(IpNet),
15 /// Match on destination port.
16 DstPort(u16),
17 /// Match on source IP CIDR.
18 SrcIpCidr(IpNet),
19 // GEOIP and FINAL are handled at the engine level, not in rule-sets.
20}
21
22/// Action to take when a rule matches.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "UPPERCASE")]
25pub enum Action {
26 /// Connect directly to the target.
27 Direct,
28 /// Reject the connection.
29 Reject,
30 /// Route through a named outbound.
31 #[serde(untagged)]
32 Outbound(String),
33}
34
35impl Action {
36 /// Check if this is the DIRECT action.
37 pub fn is_direct(&self) -> bool {
38 matches!(self, Action::Direct)
39 }
40
41 /// Check if this is the REJECT action.
42 pub fn is_reject(&self) -> bool {
43 matches!(self, Action::Reject)
44 }
45}
46
47/// A compiled rule in the engine's rule list.
48#[derive(Debug)]
49pub(crate) enum EngineRule {
50 /// Match against a named rule-set.
51 RuleSet { name: String, action: Action },
52 /// Match against a GEOIP country code.
53 GeoIp { code: String, action: Action },
54 /// Inline single rule (type + value).
55 Inline { rule: ParsedRule, action: Action },
56 /// Final catch-all rule.
57 Final { action: Action },
58}
59
60/// Context for matching a request against rules.
61#[derive(Debug)]
62pub struct MatchContext<'a> {
63 /// Target domain name (when the target is a domain).
64 pub domain: Option<&'a str>,
65 /// Target IP address (when the target is an IP or after DNS resolution).
66 pub dest_ip: Option<IpAddr>,
67 /// Target port.
68 pub dest_port: u16,
69 /// Source (client) IP address.
70 pub src_ip: IpAddr,
71}