Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clash/tests/data/config/rule-set-classical.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
payload:
- DOMAIN-REGEX,^www.twitter.com$
12 changes: 9 additions & 3 deletions clash/tests/data/config/rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
port: 8888
socks-port: "8889"
mixed-port: 8899
tproxy-port: 8900
# tproxy-port: 8900

tun:
enable: true
enable: false
device-id: "dev://utun1989"
route-all: false
gateway: "198.19.0.1/32"
Expand Down Expand Up @@ -297,14 +297,20 @@ rule-providers:
path: ./rule-set.yaml
interval: 300
behavior: domain
file-provider2:
type: file
path: ./rule-set-classical.yaml
interval: 300
behavior: classical

rules:
- IP-CIDR,1.1.1.1/32,udp-relay
- IP-CIDR,8.8.8.8/32,udp-relay
- DOMAIN-REGEX,^www.google.com$,DIRECT
- DOMAIN,google.com,ws-vmess
- DOMAIN-KEYWORD,httpbin,trojan-grpc
- DOMAIN,ipinfo.io,DIRECT
# - RULE-SET,file-provider,trojan
- RULE-SET,file-provider2,DIRECT
- GEOIP,CN,relay
- DOMAIN-SUFFIX,facebook.com,REJECT
- DOMAIN-KEYWORD,google,grpc-vmess
Expand Down
102 changes: 42 additions & 60 deletions clash_lib/src/app/router/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::app::router::rules::final_::Final;
use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration};

use hyper::Uri;
use rules::domain_regex::DomainRegex;
use tracing::{error, info, trace};

use super::{
Expand Down Expand Up @@ -235,6 +236,9 @@ pub fn map_rule_type(
RuleType::Domain { domain, target } => {
Box::new(Domain { domain, target }) as Box<dyn RuleMatcher>
}
RuleType::DomainRegex { regex, target } => {
Box::new(DomainRegex { regex, target })
}
RuleType::DomainSuffix {
domain_suffix,
target,
Expand Down Expand Up @@ -402,6 +406,10 @@ mod tests {
country_code: "CN".to_string(),
no_resolve: false,
},
RuleType::DomainRegex {
regex: regex::Regex::new(r"^regex").unwrap(),
target: "regex-match".to_string(),
},
RuleType::DomainSuffix {
domain_suffix: "t.me".to_string(),
target: "DS".to_string(),
Expand All @@ -425,65 +433,39 @@ mod tests {
)
.await;

assert_eq!(
router
.match_route(&mut Session {
destination: crate::session::SocksAddr::Domain(
"china.com".to_string(),
1111,
),
..Default::default()
})
.await
.0,
"DIRECT",
"should resolve and match IP"
);

assert_eq!(
router
.match_route(&mut Session {
destination: crate::session::SocksAddr::Domain(
"t.me".to_string(),
1111,
),
..Default::default()
})
.await
.0,
"DS",
"should match domain"
);

assert_eq!(
router
.match_route(&mut Session {
destination: crate::session::SocksAddr::Domain(
"git.io".to_string(),
1111
),
..Default::default()
})
.await
.0,
"DS2",
"should still match domain after previous rule resolved IP and non \
match"
);

assert_eq!(
router
.match_route(&mut Session {
destination: crate::session::SocksAddr::Domain(
"no-match".to_string(),
1111
),
..Default::default()
})
.await
.0,
"MATCH",
"should fallback to MATCH when nothing matched"
);
let cases = vec![
("china.com", "DIRECT", "should resolve and match IP"),
("regex", "regex-match", "should match regex"),
("t.me", "DS", "should match domain"),
(
"git.io",
"DS2",
"should still match domain after previous rule resolved IP and non \
match",
),
(
"no-match",
"MATCH",
"should fallback to MATCH when nothing matched",
),
];

for (domain, target, desc) in cases {
assert_eq!(
router
.match_route(&mut Session {
destination: crate::session::SocksAddr::Domain(
domain.to_string(),
1111
),
..Default::default()
})
.await
.0,
target,
"{}",
desc
);
}
}
}
37 changes: 37 additions & 0 deletions clash_lib/src/app/router/rules/domain_regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::{
app::router::rules::RuleMatcher,
session::{Session, SocksAddr},
};

#[derive(Clone)]
pub struct DomainRegex {
pub regex: regex::Regex,
pub target: String,
}

impl std::fmt::Display for DomainRegex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} suffix {}", self.target, self.regex)
}
}

impl RuleMatcher for DomainRegex {
fn apply(&self, sess: &Session) -> bool {
match &sess.destination {
SocksAddr::Ip(_) => false,
SocksAddr::Domain(domain, _) => self.regex.is_match(domain),
}
}

fn target(&self) -> &str {
self.target.as_str()
}

fn payload(&self) -> String {
self.regex.to_string()
}

fn type_name(&self) -> &str {
"DomainRegex"
}
}
1 change: 1 addition & 0 deletions clash_lib/src/app/router/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::session::Session;

pub mod domain;
pub mod domain_keyword;
pub mod domain_regex;
pub mod domain_suffix;
pub mod final_;
pub mod geodata;
Expand Down
13 changes: 13 additions & 0 deletions clash_lib/src/config/internal/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub enum RuleType {
domain_suffix: String,
target: String,
},
DomainRegex {
regex: regex::Regex,
target: String,
},
DomainKeyword {
domain_keyword: String,
target: String,
Expand Down Expand Up @@ -63,6 +67,7 @@ impl RuleType {
match self {
RuleType::Domain { target, .. } => target,
RuleType::DomainSuffix { target, .. } => target,
RuleType::DomainRegex { target, .. } => target,
RuleType::DomainKeyword { target, .. } => target,
RuleType::GeoIP { target, .. } => target,
RuleType::GeoSite { target, .. } => target,
Expand All @@ -84,6 +89,9 @@ impl Display for RuleType {
RuleType::Domain { domain, target } => {
write!(f, "DOMAIN,{},{}", domain, target)
}
RuleType::DomainRegex { regex, target } => {
write!(f, "DOMAIN-REGEX,{},{}", regex, target)
}
RuleType::DomainSuffix { .. } => write!(f, "DOMAIN-SUFFIX"),
RuleType::DomainKeyword { .. } => write!(f, "DOMAIN-KEYWORD"),
RuleType::GeoIP { .. } => write!(f, "GEOIP"),
Expand Down Expand Up @@ -112,6 +120,11 @@ impl RuleType {
domain: payload.to_string(),
target: target.to_string(),
}),
"DOMAIN-REGEX" => Ok(RuleType::DomainRegex {
regex: regex::Regex::new(payload)
.map_err(|e| Error::InvalidConfig(e.to_string()))?,
target: target.to_string(),
}),
"DOMAIN-SUFFIX" => Ok(RuleType::DomainSuffix {
domain_suffix: payload.to_string(),
target: target.to_string(),
Expand Down