1use std::{
6 borrow::Cow,
7 collections::HashSet,
8 fmt,
9 fs::File,
10 io::{self, BufRead, BufReader, Error},
11 net::{IpAddr, SocketAddr},
12 path::{Path, PathBuf},
13 str,
14 sync::LazyLock,
15};
16
17use ipnet::{IpNet, Ipv4Net, Ipv6Net};
18use iprange::IpRange;
19use log::{trace, warn};
20use regex::bytes::{Regex, RegexBuilder, RegexSet, RegexSetBuilder};
21
22use shadowsocks::{context::Context, relay::socks5::Address};
23
24use self::sub_domains_tree::SubDomainsTree;
25
26mod sub_domains_tree;
27
28#[derive(Debug, Copy, Clone, Eq, PartialEq)]
30pub enum Mode {
31 BlackList,
33 WhiteList,
35}
36
37#[derive(Clone)]
38struct Rules {
39 ipv4: IpRange<Ipv4Net>,
40 ipv6: IpRange<Ipv6Net>,
41 rule_regex: RegexSet,
42 rule_set: HashSet<String>,
43 rule_tree: SubDomainsTree,
44}
45
46impl fmt::Debug for Rules {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(
49 f,
50 "Rules {{ ipv4: {:?}, ipv6: {:?}, rule_regex: [",
51 self.ipv4, self.ipv6
52 )?;
53
54 let max_len = 2;
55 let has_more = self.rule_regex.len() > max_len;
56
57 for (idx, r) in self.rule_regex.patterns().iter().take(max_len).enumerate() {
58 if idx > 0 {
59 f.write_str(", ")?;
60 }
61 f.write_str(r)?;
62 }
63
64 if has_more {
65 f.write_str(", ...")?;
66 }
67
68 write!(f, "], rule_set: [")?;
69
70 let has_more = self.rule_set.len() > max_len;
71 for (idx, r) in self.rule_set.iter().take(max_len).enumerate() {
72 if idx > 0 {
73 f.write_str(", ")?;
74 }
75 f.write_str(r)?;
76 }
77
78 if has_more {
79 f.write_str(", ...")?;
80 }
81
82 write!(f, "], rule_tree: {:?} }}", self.rule_tree)
83 }
84}
85
86impl Rules {
87 fn new(
89 mut ipv4: IpRange<Ipv4Net>,
90 mut ipv6: IpRange<Ipv6Net>,
91 rule_regex: RegexSet,
92 rule_set: HashSet<String>,
93 rule_tree: SubDomainsTree,
94 ) -> Self {
95 ipv4.simplify();
97 ipv6.simplify();
98
99 Self {
100 ipv4,
101 ipv6,
102 rule_regex,
103 rule_set,
104 rule_tree,
105 }
106 }
107
108 #[allow(dead_code)]
110 fn check_address_matched(&self, addr: &Address) -> bool {
111 match *addr {
112 Address::SocketAddress(ref saddr) => self.check_ip_matched(&saddr.ip()),
113 Address::DomainNameAddress(ref domain, ..) => self.check_host_matched(domain),
114 }
115 }
116
117 fn check_ip_matched(&self, addr: &IpAddr) -> bool {
119 match addr {
120 IpAddr::V4(v4) => {
121 if self.ipv4.contains(v4) {
122 return true;
123 }
124
125 let mapped_ipv6 = v4.to_ipv6_mapped();
126 self.ipv6.contains(&mapped_ipv6)
127 }
128 IpAddr::V6(v6) => {
129 if self.ipv6.contains(v6) {
130 return true;
131 }
132
133 if let Some(mapped_ipv4) = v6.to_ipv4_mapped() {
134 return self.ipv4.contains(&mapped_ipv4);
135 }
136
137 false
138 }
139 }
140 }
141
142 fn check_host_matched(&self, host: &str) -> bool {
144 let host = host.trim_end_matches('.'); self.rule_set.contains(host) || self.rule_tree.contains(host) || self.rule_regex.is_match(host.as_bytes())
146 }
147
148 fn is_ip_empty(&self) -> bool {
150 self.ipv4.is_empty() && self.ipv6.is_empty()
151 }
152
153 fn is_host_empty(&self) -> bool {
155 self.rule_set.is_empty() && self.rule_tree.is_empty() && self.rule_regex.is_empty()
156 }
157}
158
159struct ParsingRules {
160 name: &'static str,
161 ipv4: IpRange<Ipv4Net>,
162 ipv6: IpRange<Ipv6Net>,
163 rules_regex: Vec<String>,
164 rules_set: HashSet<String>,
165 rules_tree: SubDomainsTree,
166}
167
168impl ParsingRules {
169 fn new(name: &'static str) -> Self {
170 Self {
171 name,
172 ipv4: IpRange::new(),
173 ipv6: IpRange::new(),
174 rules_regex: Vec::new(),
175 rules_set: HashSet::new(),
176 rules_tree: SubDomainsTree::new(),
177 }
178 }
179
180 fn add_ipv4_rule(&mut self, rule: impl Into<Ipv4Net>) {
181 let rule = rule.into();
182 trace!("IPV4-RULE {}", rule);
183 self.ipv4.add(rule);
184 }
185
186 fn add_ipv6_rule(&mut self, rule: impl Into<Ipv6Net>) {
187 let rule = rule.into();
188 trace!("IPV6-RULE {}", rule);
189 self.ipv6.add(rule);
190 }
191
192 fn add_regex_rule(&mut self, mut rule: String) {
193 static TREE_SET_RULE_EQUIV: LazyLock<Regex> = LazyLock::new(|| {
194 RegexBuilder::new(
195 r#"^(?:(?:\((?:\?:)?\^\|\\\.\)|(?:\^\.(?:\+|\*))?\\\.)((?:[\w-]+(?:\\\.)?)+)|\^((?:[\w-]+(?:\\\.)?)+))\$?$"#,
196 )
197 .unicode(false)
198 .build()
199 .unwrap()
200 });
201
202 if let Some(caps) = TREE_SET_RULE_EQUIV.captures(rule.as_bytes()) {
203 if let Some(tree_rule) = caps.get(1) {
204 if let Ok(tree_rule) = str::from_utf8(tree_rule.as_bytes()) {
205 let tree_rule = tree_rule.replace("\\.", ".");
206 if self.add_tree_rule_inner(&tree_rule).is_ok() {
207 trace!("REGEX-RULE {} => TREE-RULE {}", rule, tree_rule);
208 return;
209 }
210 }
211 } else if let Some(set_rule) = caps.get(2)
212 && let Ok(set_rule) = str::from_utf8(set_rule.as_bytes()) {
213 let set_rule = set_rule.replace("\\.", ".");
214 if self.add_set_rule_inner(&set_rule).is_ok() {
215 trace!("REGEX-RULE {} => SET-RULE {}", rule, set_rule);
216 return;
217 }
218 }
219 }
220
221 trace!("REGEX-RULE {}", rule);
222
223 rule.make_ascii_lowercase();
224
225 self.rules_regex.push(rule);
228 }
229
230 #[inline]
231 fn add_set_rule(&mut self, rule: &str) -> io::Result<()> {
232 trace!("SET-RULE {}", rule);
233 self.add_set_rule_inner(rule)
234 }
235
236 fn add_set_rule_inner(&mut self, rule: &str) -> io::Result<()> {
237 self.rules_set.insert(self.check_is_ascii(rule)?.to_ascii_lowercase());
238 Ok(())
239 }
240
241 #[inline]
242 fn add_tree_rule(&mut self, rule: &str) -> io::Result<()> {
243 trace!("TREE-RULE {}", rule);
244 self.add_tree_rule_inner(rule)
245 }
246
247 fn add_tree_rule_inner(&mut self, rule: &str) -> io::Result<()> {
248 self.rules_tree.insert(self.check_is_ascii(rule)?);
250 Ok(())
251 }
252
253 fn check_is_ascii<'a>(&self, str: &'a str) -> io::Result<&'a str> {
254 if str.is_ascii() {
255 Ok(str.trim_end_matches('.'))
257 } else {
258 Err(Error::other(format!(
259 "{} parsing error: Unicode not allowed here `{}`",
260 self.name, str
261 )))
262 }
263 }
264
265 fn compile_regex(name: &'static str, regex_rules: Vec<String>) -> io::Result<RegexSet> {
266 const REGEX_SIZE_LIMIT: usize = usize::MAX;
267 RegexSetBuilder::new(regex_rules)
268 .size_limit(REGEX_SIZE_LIMIT)
269 .unicode(false)
270 .build()
271 .map_err(|err| Error::other(format!("{name} regex error: {err}")))
272 }
273
274 fn into_rules(self) -> io::Result<Rules> {
275 Ok(Rules::new(
276 self.ipv4,
277 self.ipv6,
278 Self::compile_regex(self.name, self.rules_regex)?,
279 self.rules_set,
280 self.rules_tree,
281 ))
282 }
283}
284
285#[derive(Debug, Clone)]
338pub struct AccessControl {
339 outbound_block: Rules,
340 outbound_allow: Rules,
341 black_list: Rules,
342 white_list: Rules,
343 mode: Mode,
344 outbound_mode: Mode,
345 file_path: PathBuf,
346}
347
348impl AccessControl {
349 pub fn load_from_file<P: AsRef<Path>>(p: P) -> io::Result<Self> {
351 trace!("ACL loading from {:?}", p.as_ref());
352
353 let file_path_ref = p.as_ref();
354 let file_path = file_path_ref.to_path_buf();
355
356 let fp = File::open(file_path_ref)?;
357 let r = BufReader::new(fp);
358
359 let mut mode = Mode::BlackList;
360 let mut outbound_mode = Mode::BlackList;
361
362 let mut outbound_block = ParsingRules::new("[outbound_block_list]");
363 let mut outbound_allow = ParsingRules::new("[outbound_allow_list]");
364 let mut bypass = ParsingRules::new("[black_list] or [bypass_list]");
365 let mut proxy = ParsingRules::new("[white_list] or [proxy_list]");
366 let mut curr = &mut bypass;
367
368 trace!("ACL parsing start from mode {:?} and black_list / bypass_list", mode);
369
370 for line in r.lines() {
371 let line = line?;
372 if line.is_empty() {
373 continue;
374 }
375
376 if line.starts_with('#') {
378 continue;
379 }
380
381 let line = line.trim();
382
383 if !line.is_ascii() {
384 warn!("ACL rule {} containing non-ASCII characters, skipped", line);
385 continue;
386 }
387
388 if let Some(rule) = line.strip_prefix("||") {
389 curr.add_tree_rule(rule)?;
390 continue;
391 }
392
393 if let Some(rule) = line.strip_prefix('|') {
394 curr.add_set_rule(rule)?;
395 continue;
396 }
397
398 match line {
399 "[reject_all]" | "[bypass_all]" => {
400 mode = Mode::WhiteList;
401 trace!("switch to mode {:?}", mode);
402 }
403 "[accept_all]" | "[proxy_all]" => {
404 mode = Mode::BlackList;
405 trace!("switch to mode {:?}", mode);
406 }
407 "[outbound_block_all]" => {
408 outbound_mode = Mode::WhiteList;
409 trace!("switch to outbound_mode {:?}", outbound_mode);
410 }
411 "[outbound_allow_all]" => {
412 outbound_mode = Mode::BlackList;
413 trace!("switch to outbound_mode {:?}", outbound_mode);
414 }
415 "[outbound_block_list]" => {
416 curr = &mut outbound_block;
417 trace!("loading outbound_block_list");
418 }
419 "[outbound_allow_list]" => {
420 curr = &mut outbound_allow;
421 trace!("loading outbound_allow_list");
422 }
423 "[black_list]" | "[bypass_list]" => {
424 curr = &mut bypass;
425 trace!("loading black_list / bypass_list");
426 }
427 "[white_list]" | "[proxy_list]" => {
428 curr = &mut proxy;
429 trace!("loading white_list / proxy_list");
430 }
431 _ => {
432 match line.parse::<IpNet>() {
433 Ok(IpNet::V4(v4)) => {
434 curr.add_ipv4_rule(v4);
435 }
436 Ok(IpNet::V6(v6)) => {
437 curr.add_ipv6_rule(v6);
438 }
439 Err(..) => {
440 match line.parse::<IpAddr>() {
442 Ok(IpAddr::V4(v4)) => {
443 curr.add_ipv4_rule(v4);
444 }
445 Ok(IpAddr::V6(v6)) => {
446 curr.add_ipv6_rule(v6);
447 }
448 Err(..) => {
449 curr.add_regex_rule(line.to_owned());
450 }
451 }
452 }
453 }
454 }
455 }
456 }
457
458 Ok(Self {
459 outbound_block: outbound_block.into_rules()?,
460 outbound_allow: outbound_allow.into_rules()?,
461 black_list: bypass.into_rules()?,
462 white_list: proxy.into_rules()?,
463 mode,
464 outbound_mode,
465 file_path,
466 })
467 }
468
469 pub fn file_path(&self) -> &Path {
471 &self.file_path
472 }
473
474 pub fn check_host_in_proxy_list(&self, host: &str) -> Option<bool> {
482 let host = Self::convert_to_ascii(host);
483 self.check_ascii_host_in_proxy_list(&host)
484 }
485
486 pub fn check_ascii_host_in_proxy_list(&self, host: &str) -> Option<bool> {
494 if self.white_list.check_host_matched(host) {
496 return Some(true);
497 }
498 if self.black_list.check_host_matched(host) {
500 return Some(false);
501 }
502 None
503 }
504
505 #[inline]
507 pub fn is_ip_empty(&self) -> bool {
508 self.black_list.is_ip_empty() && self.white_list.is_ip_empty()
509 }
510
511 #[inline]
513 pub fn is_host_empty(&self) -> bool {
514 self.black_list.is_host_empty() && self.white_list.is_host_empty()
515 }
516
517 pub fn check_ip_in_proxy_list(&self, ip: &IpAddr) -> bool {
519 if self.black_list.check_ip_matched(ip) {
520 return false;
522 }
523 if self.white_list.check_ip_matched(ip) {
524 return true;
526 }
527 self.is_default_in_proxy_list()
528 }
529
530 #[inline]
536 pub fn is_default_in_proxy_list(&self) -> bool {
537 match self.mode {
538 Mode::BlackList => true,
539 Mode::WhiteList => false,
540 }
541 }
542
543 fn convert_to_ascii(host: &str) -> Cow<'_, str> {
546 idna::domain_to_ascii(host)
547 .map(From::from)
548 .unwrap_or_else(|_| host.into())
549 }
550
551 pub async fn check_target_bypassed(&self, context: &Context, addr: &Address) -> bool {
555 match *addr {
556 Address::SocketAddress(ref addr) => !self.check_ip_in_proxy_list(&addr.ip()),
557 Address::DomainNameAddress(ref host, port) => {
559 if let Some(value) = self.check_host_in_proxy_list(host) {
560 return !value;
561 }
562
563 let (check_list, bypass_if_matched) = match self.mode {
566 Mode::BlackList => (&self.black_list, true),
567 Mode::WhiteList => (&self.white_list, false),
568 };
569
570 if check_list.is_ip_empty() {
571 return !self.is_default_in_proxy_list();
572 }
573
574 if let Ok(vaddr) = context.dns_resolve(host, port).await {
575 for addr in vaddr {
576 let ip = addr.ip();
577 if check_list.check_ip_matched(&ip) {
578 return bypass_if_matched;
579 }
580 }
581 }
582
583 !self.is_default_in_proxy_list()
584 }
585 }
586 }
587
588 pub fn check_client_blocked(&self, addr: &SocketAddr) -> bool {
590 match self.mode {
591 Mode::BlackList => {
592 self.black_list.check_ip_matched(&addr.ip())
594 }
595 Mode::WhiteList => {
596 !self.white_list.check_ip_matched(&addr.ip())
598 }
599 }
600 }
601
602 pub async fn check_outbound_blocked(&self, context: &Context, outbound: &Address) -> bool {
607 match outbound {
608 Address::SocketAddress(saddr) => self.check_outbound_ip_blocked(&saddr.ip()),
609 Address::DomainNameAddress(host, port) => {
610 let ascii_host = Self::convert_to_ascii(host);
611 if self.outbound_block.check_host_matched(&ascii_host) {
612 return true; }
614 if self.outbound_allow.check_host_matched(&ascii_host) {
615 return false; }
617
618 let (check_rule, block_if_matched) = match self.outbound_mode {
624 Mode::BlackList => (&self.outbound_block, true),
625 Mode::WhiteList => (&self.outbound_allow, false),
626 };
627
628 if check_rule.is_ip_empty() {
629 return self.is_outbound_default_blocked();
631 }
632
633 if let Ok(vaddr) = context.dns_resolve(host, *port).await {
634 for addr in vaddr {
635 let ip = addr.ip();
636 if check_rule.check_ip_matched(&ip) {
637 return block_if_matched;
638 }
639 }
640 }
641
642 self.is_outbound_default_blocked()
643 }
644 }
645 }
646
647 fn check_outbound_ip_blocked(&self, ip: &IpAddr) -> bool {
648 if self.outbound_block.check_ip_matched(ip) {
649 return true;
651 }
652 if self.outbound_allow.check_ip_matched(ip) {
653 return false;
655 }
656 self.is_outbound_default_blocked()
658 }
659
660 #[inline]
661 fn is_outbound_default_blocked(&self) -> bool {
662 match self.outbound_mode {
663 Mode::BlackList => false,
664 Mode::WhiteList => true,
665 }
666 }
667}