@@ -5,20 +5,40 @@ use std::sync::Arc;
55use ipnet:: IpNet ;
66use prefix_trie:: joint:: set:: JointPrefixSet ;
77
8+ #[ derive( Clone , Debug , Default , PartialEq , Eq ) ]
9+ pub enum IpBlacklistLoadStatus {
10+ #[ default]
11+ NotSelected ,
12+ Loading ,
13+ Loaded {
14+ ip_count : usize ,
15+ network_count : usize ,
16+ ignored_lines : usize ,
17+ } ,
18+ FileReadError ,
19+ NoValidEntries {
20+ ignored_lines : usize ,
21+ } ,
22+ }
23+
824#[ derive( Clone , Default , Debug ) ]
925pub struct IpBlacklist {
1026 ips : Arc < HashSet < IpAddr > > ,
1127 networks : Arc < JointPrefixSet < IpNet > > ,
12- is_loading : bool ,
28+ status : IpBlacklistLoadStatus ,
1329}
1430
1531impl IpBlacklist {
1632 pub async fn from_file ( path : String ) -> Self {
1733 let Ok ( buf) = tokio:: fs:: read_to_string ( & path) . await else {
18- return IpBlacklist :: default ( ) ;
34+ return IpBlacklist {
35+ status : IpBlacklistLoadStatus :: FileReadError ,
36+ ..IpBlacklist :: default ( )
37+ } ;
1938 } ;
2039 let mut ips = HashSet :: new ( ) ;
2140 let mut networks = JointPrefixSet :: new ( ) ;
41+ let mut ignored_lines = 0 ;
2242 for line in buf. lines ( ) {
2343 let Some ( first) = line. split_whitespace ( ) . next ( ) else {
2444 continue ;
@@ -28,12 +48,26 @@ impl IpBlacklist {
2848 ips. insert ( ip) ;
2949 } else if let Ok ( network) = first. parse :: < IpNet > ( ) {
3050 networks. insert ( network) ;
51+ } else {
52+ ignored_lines += 1 ;
3153 }
3254 }
55+ let ip_count = ips. len ( ) ;
56+ let network_count = networks. len ( ) ;
57+ let status = if ip_count == 0 && network_count == 0 {
58+ IpBlacklistLoadStatus :: NoValidEntries { ignored_lines }
59+ } else {
60+ IpBlacklistLoadStatus :: Loaded {
61+ ip_count,
62+ network_count,
63+ ignored_lines,
64+ }
65+ } ;
66+
3367 IpBlacklist {
3468 ips : Arc :: new ( ips) ,
3569 networks : Arc :: new ( networks) ,
36- is_loading : false ,
70+ status ,
3771 }
3872 }
3973
@@ -42,15 +76,22 @@ impl IpBlacklist {
4276 }
4377
4478 pub fn is_invalid ( & self ) -> bool {
45- self . ips . is_empty ( ) && self . networks . is_empty ( ) && !self . is_loading
79+ matches ! (
80+ self . status,
81+ IpBlacklistLoadStatus :: FileReadError | IpBlacklistLoadStatus :: NoValidEntries { .. }
82+ )
4683 }
4784
4885 pub fn is_loading ( & self ) -> bool {
49- self . is_loading
86+ matches ! ( self . status, IpBlacklistLoadStatus :: Loading )
87+ }
88+
89+ pub fn status ( & self ) -> & IpBlacklistLoadStatus {
90+ & self . status
5091 }
5192
5293 pub fn start_loading ( & mut self ) {
53- self . is_loading = true ;
94+ self . status = IpBlacklistLoadStatus :: Loading ;
5495 }
5596}
5697
@@ -68,6 +109,14 @@ mod tests {
68109 assert ! ( !blacklist. is_loading( ) ) ;
69110 assert_eq ! ( blacklist. ips. len( ) , 4 ) ;
70111 assert_eq ! ( blacklist. networks. len( ) , 0 ) ;
112+ assert_eq ! (
113+ blacklist. status( ) ,
114+ & IpBlacklistLoadStatus :: Loaded {
115+ ip_count: 4 ,
116+ network_count: 0 ,
117+ ignored_lines: 4 ,
118+ }
119+ ) ;
71120
72121 assert ! ( blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 8 , 8 , 8 , 8 ) ) ) ) ;
73122 assert ! ( blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 1 , 2 , 3 , 255 ) ) ) ) ;
@@ -89,6 +138,10 @@ mod tests {
89138 assert ! ( !blacklist. is_loading( ) ) ;
90139 assert_eq ! ( blacklist. ips. len( ) , 0 ) ;
91140 assert_eq ! ( blacklist. networks. len( ) , 0 ) ;
141+ assert_eq ! (
142+ blacklist. status( ) ,
143+ & IpBlacklistLoadStatus :: NoValidEntries { ignored_lines: 6 }
144+ ) ;
92145
93146 assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 8 , 8 , 8 , 8 ) ) ) ) ;
94147 assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 0 , 0 , 0 , 0 ) ) ) ) ;
@@ -106,6 +159,14 @@ mod tests {
106159 assert ! ( !blacklist. is_loading( ) ) ;
107160 assert_eq ! ( blacklist. ips. len( ) , 2 ) ;
108161 assert_eq ! ( blacklist. networks. len( ) , 4 ) ;
162+ assert_eq ! (
163+ blacklist. status( ) ,
164+ & IpBlacklistLoadStatus :: Loaded {
165+ ip_count: 2 ,
166+ network_count: 4 ,
167+ ignored_lines: 3 ,
168+ }
169+ ) ;
109170
110171 assert ! ( blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 8 , 8 , 8 , 8 ) ) ) ) ;
111172 assert ! ( blacklist. contains( & "2001:db8::1" . parse:: <IpAddr >( ) . unwrap( ) ) ) ;
@@ -137,6 +198,14 @@ mod tests {
137198 assert ! ( !blacklist. is_loading( ) ) ;
138199 assert_eq ! ( blacklist. ips. len( ) , 0 ) ;
139200 assert_eq ! ( blacklist. networks. len( ) , 1 ) ;
201+ assert_eq ! (
202+ blacklist. status( ) ,
203+ & IpBlacklistLoadStatus :: Loaded {
204+ ip_count: 0 ,
205+ network_count: 1 ,
206+ ignored_lines: 0 ,
207+ }
208+ ) ;
140209
141210 assert ! ( blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 1 , 2 , 3 , 1 ) ) ) ) ;
142211 assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 1 , 2 , 4 , 1 ) ) ) ) ;
@@ -170,4 +239,28 @@ mod tests {
170239 assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 209 , 186 , 237 , 0 ) ) ) ) ;
171240 assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 209 , 233 , 160 , 0 ) ) ) ) ;
172241 }
242+
243+ #[ tokio:: test]
244+ async fn test_ip_blacklist_read_error ( ) {
245+ let blacklist =
246+ IpBlacklist :: from_file ( "resources/test/does_not_exist_blacklist.txt" . to_string ( ) ) . await ;
247+
248+ assert ! ( blacklist. is_invalid( ) ) ;
249+ assert ! ( !blacklist. is_loading( ) ) ;
250+ assert_eq ! ( blacklist. ips. len( ) , 0 ) ;
251+ assert_eq ! ( blacklist. networks. len( ) , 0 ) ;
252+ assert_eq ! ( blacklist. status( ) , & IpBlacklistLoadStatus :: FileReadError ) ;
253+ assert ! ( !blacklist. contains( & IpAddr :: V4 ( Ipv4Addr :: new( 8 , 8 , 8 , 8 ) ) ) ) ;
254+ }
255+
256+ #[ test]
257+ fn test_ip_blacklist_loading_status ( ) {
258+ let mut blacklist = IpBlacklist :: default ( ) ;
259+
260+ blacklist. start_loading ( ) ;
261+
262+ assert ! ( blacklist. is_loading( ) ) ;
263+ assert ! ( !blacklist. is_invalid( ) ) ;
264+ assert_eq ! ( blacklist. status( ) , & IpBlacklistLoadStatus :: Loading ) ;
265+ }
173266}
0 commit comments