@@ -256,9 +256,10 @@ impl ParsingRules {
256
256
// Remove the last `.` of FQDN
257
257
Ok ( str. trim_end_matches ( '.' ) )
258
258
} else {
259
- Err ( Error :: other (
260
- format ! ( "{} parsing error: Unicode not allowed here `{}`" , self . name, str ) ,
261
- ) )
259
+ Err ( Error :: other ( format ! (
260
+ "{} parsing error: Unicode not allowed here `{}`" ,
261
+ self . name, str
262
+ ) ) )
262
263
}
263
264
}
264
265
@@ -309,11 +310,14 @@ impl ParsingRules {
309
310
/// * `[bypass_list]` - Rules for connecting directly
310
311
/// * `[proxy_list]` - Rules for connecting through proxies
311
312
/// - For remote servers (`ssserver`)
312
- /// * `[reject_all]` - ACL runs in `BlackList ` mode.
313
- /// * `[accept_all]` - ACL runs in `WhiteList ` mode.
313
+ /// * `[reject_all]` - ACL runs in `WhiteList ` mode.
314
+ /// * `[accept_all]` - ACL runs in `BlackList ` mode.
314
315
/// * `[black_list]` - Rules for rejecting
315
316
/// * `[white_list]` - Rules for allowing
317
+ /// * `[outbound_block_all]` - ACL runs in `WhiteList` mode for outbound addresses.
318
+ /// * `[outbound_allow_all]` - ACL runs in `BlackList` mode for outbound addresses.
316
319
/// * `[outbound_block_list]` - Rules for blocking outbound addresses.
320
+ /// * `[outbound_allow_list]` - Rules for allowing outbound addresses.
317
321
///
318
322
/// ## Mode
319
323
///
@@ -334,9 +338,11 @@ impl ParsingRules {
334
338
#[ derive( Debug , Clone ) ]
335
339
pub struct AccessControl {
336
340
outbound_block : Rules ,
341
+ outbound_allow : Rules ,
337
342
black_list : Rules ,
338
343
white_list : Rules ,
339
344
mode : Mode ,
345
+ outbound_mode : Mode ,
340
346
file_path : PathBuf ,
341
347
}
342
348
@@ -352,8 +358,10 @@ impl AccessControl {
352
358
let r = BufReader :: new ( fp) ;
353
359
354
360
let mut mode = Mode :: BlackList ;
361
+ let mut outbound_mode = Mode :: BlackList ;
355
362
356
363
let mut outbound_block = ParsingRules :: new ( "[outbound_block_list]" ) ;
364
+ let mut outbound_allow = ParsingRules :: new ( "[outbound_allow_list]" ) ;
357
365
let mut bypass = ParsingRules :: new ( "[black_list] or [bypass_list]" ) ;
358
366
let mut proxy = ParsingRules :: new ( "[white_list] or [proxy_list]" ) ;
359
367
let mut curr = & mut bypass;
@@ -397,10 +405,22 @@ impl AccessControl {
397
405
mode = Mode :: BlackList ;
398
406
trace ! ( "switch to mode {:?}" , mode) ;
399
407
}
408
+ "[outbound_block_all]" => {
409
+ outbound_mode = Mode :: WhiteList ;
410
+ trace ! ( "switch to outbound_mode {:?}" , outbound_mode) ;
411
+ }
412
+ "[outbound_allow_all]" => {
413
+ outbound_mode = Mode :: BlackList ;
414
+ trace ! ( "switch to outbound_mode {:?}" , outbound_mode) ;
415
+ }
400
416
"[outbound_block_list]" => {
401
417
curr = & mut outbound_block;
402
418
trace ! ( "loading outbound_block_list" ) ;
403
419
}
420
+ "[outbound_allow_list]" => {
421
+ curr = & mut outbound_allow;
422
+ trace ! ( "loading outbound_allow_list" ) ;
423
+ }
404
424
"[black_list]" | "[bypass_list]" => {
405
425
curr = & mut bypass;
406
426
trace ! ( "loading black_list / bypass_list" ) ;
@@ -438,9 +458,11 @@ impl AccessControl {
438
458
439
459
Ok ( Self {
440
460
outbound_block : outbound_block. into_rules ( ) ?,
461
+ outbound_allow : outbound_allow. into_rules ( ) ?,
441
462
black_list : bypass. into_rules ( ) ?,
442
463
white_list : proxy. into_rules ( ) ?,
443
464
mode,
465
+ outbound_mode,
444
466
file_path,
445
467
} )
446
468
}
@@ -482,31 +504,36 @@ impl AccessControl {
482
504
}
483
505
484
506
/// If there are no IP rules
507
+ #[ inline]
485
508
pub fn is_ip_empty ( & self ) -> bool {
486
- match self . mode {
487
- Mode :: BlackList => self . black_list . is_ip_empty ( ) ,
488
- Mode :: WhiteList => self . white_list . is_ip_empty ( ) ,
489
- }
509
+ self . black_list . is_ip_empty ( ) && self . white_list . is_ip_empty ( )
490
510
}
491
511
492
512
/// If there are no domain name rules
513
+ #[ inline]
493
514
pub fn is_host_empty ( & self ) -> bool {
494
515
self . black_list . is_host_empty ( ) && self . white_list . is_host_empty ( )
495
516
}
496
517
497
518
/// Check if `IpAddr` should be proxied
498
519
pub fn check_ip_in_proxy_list ( & self , ip : & IpAddr ) -> bool {
499
- match self . mode {
500
- Mode :: BlackList => !self . black_list . check_ip_matched ( ip) ,
501
- Mode :: WhiteList => self . white_list . check_ip_matched ( ip) ,
520
+ if self . black_list . check_ip_matched ( ip) {
521
+ // If IP is in black_list, it should be bypassed
522
+ return false ;
523
+ }
524
+ if self . white_list . check_ip_matched ( ip) {
525
+ // If IP is in white_list, it should be proxied
526
+ return true ;
502
527
}
528
+ self . is_default_in_proxy_list ( )
503
529
}
504
530
505
531
/// Default mode
506
532
///
507
533
/// Default behavior for hosts that are not configured
508
534
/// - `true` - Proxied
509
535
/// - `false` - Bypassed
536
+ #[ inline]
510
537
pub fn is_default_in_proxy_list ( & self ) -> bool {
511
538
match self . mode {
512
539
Mode :: BlackList => true ,
@@ -543,7 +570,7 @@ impl AccessControl {
543
570
}
544
571
}
545
572
}
546
- false
573
+ ! self . is_default_in_proxy_list ( )
547
574
}
548
575
}
549
576
}
@@ -556,7 +583,7 @@ impl AccessControl {
556
583
self . black_list . check_ip_matched ( & addr. ip ( ) )
557
584
}
558
585
Mode :: WhiteList => {
559
- // Only clients in white_list will be proxied
586
+ // Only clients not in white_list will be blocked
560
587
!self . white_list . check_ip_matched ( & addr. ip ( ) )
561
588
}
562
589
}
@@ -568,22 +595,59 @@ impl AccessControl {
568
595
/// resolved addresses are checked in the `lookup_outbound_then!` macro
569
596
pub async fn check_outbound_blocked ( & self , context : & Context , outbound : & Address ) -> bool {
570
597
match outbound {
571
- Address :: SocketAddress ( saddr) => self . outbound_block . check_ip_matched ( & saddr. ip ( ) ) ,
598
+ Address :: SocketAddress ( saddr) => self . check_outbound_ip_blocked ( & saddr. ip ( ) ) ,
572
599
Address :: DomainNameAddress ( host, port) => {
573
- if self . outbound_block . check_host_matched ( & Self :: convert_to_ascii ( host) ) {
574
- return true ;
600
+ let ascii_host = Self :: convert_to_ascii ( host) ;
601
+ if self . outbound_block . check_host_matched ( & ascii_host) {
602
+ return true ; // Blocked by config
603
+ }
604
+ if self . outbound_allow . check_host_matched ( & ascii_host) {
605
+ return false ; // Allowed by config
606
+ }
607
+
608
+ // If no domain name rules matched,
609
+ // we need to resolve the hostname to IP addresses
610
+ if self . is_outbound_ip_empty ( ) {
611
+ // If there are no IP rules, use the default mode
612
+ return self . is_outbound_default_blocked ( ) ;
575
613
}
576
614
577
615
if let Ok ( vaddr) = context. dns_resolve ( host, * port) . await {
578
616
for addr in vaddr {
579
- if self . outbound_block . check_ip_matched ( & addr. ip ( ) ) {
617
+ if self . check_outbound_ip_blocked ( & addr. ip ( ) ) {
580
618
return true ;
581
619
}
582
620
}
583
621
}
584
622
585
- false
623
+ self . is_outbound_default_blocked ( )
586
624
}
587
625
}
588
626
}
627
+
628
+ fn check_outbound_ip_blocked ( & self , ip : & IpAddr ) -> bool {
629
+ if self . outbound_block . check_ip_matched ( ip) {
630
+ // If IP is in outbound_block, it should be blocked
631
+ return true ;
632
+ }
633
+ if self . outbound_allow . check_ip_matched ( ip) {
634
+ // If IP is in outbound_allow, it should be allowed
635
+ return false ;
636
+ }
637
+ // If IP is not in any list, check the default mode
638
+ self . is_outbound_default_blocked ( )
639
+ }
640
+
641
+ #[ inline]
642
+ fn is_outbound_default_blocked ( & self ) -> bool {
643
+ match self . outbound_mode {
644
+ Mode :: BlackList => false ,
645
+ Mode :: WhiteList => true ,
646
+ }
647
+ }
648
+
649
+ #[ inline]
650
+ fn is_outbound_ip_empty ( & self ) -> bool {
651
+ self . outbound_block . is_ip_empty ( ) && self . outbound_allow . is_ip_empty ( )
652
+ }
589
653
}
0 commit comments