@@ -2,8 +2,10 @@ use anyhow::anyhow;
2
2
use futures:: { select, FutureExt } ;
3
3
use leak_checker:: traceroute:: TracerouteOpt ;
4
4
pub use leak_checker:: LeakInfo ;
5
+ use mullvad_types:: TUNNEL_FWMARK ;
5
6
use std:: net:: IpAddr ;
6
7
use std:: time:: Duration ;
8
+ use talpid_routing:: RouteManagerHandle ;
7
9
use talpid_types:: tunnel:: TunnelStateTransition ;
8
10
use tokio:: sync:: mpsc;
9
11
@@ -15,6 +17,7 @@ pub struct LeakChecker {
15
17
/// [LeakChecker] internal task state.
16
18
struct Task {
17
19
events_rx : mpsc:: UnboundedReceiver < TaskEvent > ,
20
+ route_manager : RouteManagerHandle ,
18
21
callbacks : Vec < Box < dyn LeakCheckerCallback > > ,
19
22
}
20
23
@@ -36,11 +39,12 @@ pub trait LeakCheckerCallback: Send + 'static {
36
39
}
37
40
38
41
impl LeakChecker {
39
- pub fn new ( ) -> Self {
42
+ pub fn new ( route_manager : RouteManagerHandle ) -> Self {
40
43
let ( task_event_tx, events_rx) = mpsc:: unbounded_channel ( ) ;
41
44
42
45
let task = Task {
43
46
events_rx,
47
+ route_manager,
44
48
callbacks : vec ! [ ] ,
45
49
} ;
46
50
@@ -56,10 +60,12 @@ impl LeakChecker {
56
60
self . send ( TaskEvent :: NewTunnelState ( tunnel_state) )
57
61
}
58
62
63
+ /// Call `callback` if a leak is detected.
59
64
pub fn add_leak_callback ( & mut self , callback : impl LeakCheckerCallback ) {
60
65
self . send ( TaskEvent :: AddCallback ( Box :: new ( callback) ) )
61
66
}
62
67
68
+ /// Send a [TaskEvent] to the running [Task];
63
69
fn send ( & mut self , event : TaskEvent ) {
64
70
if self . task_event_tx . send ( event) . is_err ( ) {
65
71
panic ! ( "LeakChecker unexpectedly closed" ) ;
@@ -92,25 +98,15 @@ impl Task {
92
98
} ;
93
99
94
100
let ping_destination = tunnel. endpoint . address . ip ( ) ;
101
+ let route_manager = self . route_manager . clone ( ) ;
102
+ let leak_test = async {
103
+ // Give the connection a little time to settle before starting the test.
104
+ // TODO: is this necessary? is there some better way?
105
+ // TODO: ether remove this or add some concrete motivation.
106
+ tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
95
107
96
- // TODO (linux):
97
- // Use get_destination_route(ip, Some(fwmark)) to figure out default interface.
98
- // where ip is some unused example public ip, or maybe the relay ip
99
-
100
- // TODO (android):
101
- // Maybe connectivity monitor?
102
- // It should be possible somehow. `ifconfig` can print interfaces.
103
- // needs further investigation
104
-
105
- // TODO (macos):
106
- // get_default_route in route manager
107
-
108
- // TODO (windows):
109
- // Use default route monitor thingy. It should contain interfaces.
110
- // Can maybe use callback to subscribe for updates
111
- // get_best_route
112
-
113
- let interface = "wlan0" ; // TODO
108
+ check_for_leaks ( & route_manager, ping_destination) . await
109
+ } ;
114
110
115
111
// Make sure the tunnel state doesn't change while we're doing the leak test.
116
112
// If that happens, then our results might be invalid.
@@ -134,15 +130,6 @@ impl Task {
134
130
}
135
131
} ;
136
132
137
- let leak_test = async {
138
- // Give the connection a little time to settle before starting the test.
139
- // TODO: is this necessary? is there some better way?
140
- // TODO: ether remove this or add some concrete motivation.
141
- tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
142
-
143
- check_for_leaks ( interface, ping_destination) . await
144
- } ;
145
-
146
133
let leak_result = select ! {
147
134
// If tunnel state changes, restart the test.
148
135
_ = another_tunnel_state. fuse( ) => continue ' leak_test,
@@ -173,7 +160,51 @@ impl Task {
173
160
}
174
161
}
175
162
176
- async fn check_for_leaks ( interface : & str , destination : IpAddr ) -> anyhow:: Result < Option < LeakInfo > > {
163
+ async fn check_for_leaks (
164
+ route_manager : & RouteManagerHandle ,
165
+ destination : IpAddr ,
166
+ ) -> anyhow:: Result < Option < LeakInfo > > {
167
+ // TODO (linux):
168
+ // Use get_destination_route(ip, Some(fwmark)) to figure out default interface.
169
+ // where ip is some unused example public ip, or maybe the relay ip
170
+ #[ cfg( target_os = "linux" ) ]
171
+ let interface = {
172
+ let Ok ( Some ( route) ) = route_manager
173
+ . get_destination_route ( destination, Some ( TUNNEL_FWMARK ) )
174
+ . await
175
+ else {
176
+ todo ! ( "no route to relay?" ) ;
177
+ } ;
178
+
179
+ route
180
+ . get_node ( )
181
+ . get_device ( )
182
+ . expect ( "no device for default route??" )
183
+ . to_string ( )
184
+ } ;
185
+
186
+ // TODO (android):
187
+ // Maybe connectivity monitor?
188
+ // It should be possible somehow. `ifconfig` can print interfaces.
189
+ // needs further investigation
190
+ #[ cfg( target_os = "android" ) ]
191
+ let interface = todo ! ( "get default interface" ) ;
192
+
193
+ // TODO (macos):
194
+ // get_default_route in route manager
195
+ #[ cfg( target_os = "macos" ) ]
196
+ let interface = todo ! ( "get default interface" ) ;
197
+
198
+ // TODO (windows):
199
+ // Use default route monitor thingy. It should contain interfaces.
200
+ // Can maybe use callback to subscribe for updates
201
+ // get_best_route
202
+ #[ cfg( target_os = "macos" ) ]
203
+ let interface = todo ! ( "get default interface" ) ;
204
+
205
+ log:: debug!( "attempting to leak traffic on interface {interface:?} to {destination}" ) ;
206
+
207
+ // TODO: use UDP on windows
177
208
leak_checker:: traceroute:: try_run_leak_test ( & TracerouteOpt {
178
209
interface : interface. to_string ( ) ,
179
210
destination,
0 commit comments