@@ -7,7 +7,7 @@ use std::{
77
88use clap:: Parser ;
99use error;
10- use tokio:: runtime:: Runtime ;
10+ use tokio:: { runtime:: Runtime , time :: sleep } ;
1111use windows_service:: {
1212 define_windows_service,
1313 service:: {
@@ -20,7 +20,8 @@ use windows_service::{
2020
2121use crate :: {
2222 enterprise:: service_locations:: {
23- windows:: watch_for_login_logoff, ServiceLocationError , ServiceLocationManager ,
23+ windows:: { watch_for_login_logoff, watch_for_network_change} ,
24+ ServiceLocationError , ServiceLocationManager ,
2425 } ,
2526 service:: {
2627 config:: Config ,
@@ -32,6 +33,8 @@ use crate::{
3233static SERVICE_NAME : & str = "DefguardService" ;
3334const SERVICE_TYPE : ServiceType = ServiceType :: OWN_PROCESS ;
3435const LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS : Duration = Duration :: from_secs ( 5 ) ;
36+ const SERVICE_LOCATION_CONNECT_RETRY_COUNT : u32 = 5 ;
37+ const SERVICE_LOCATION_CONNECT_RETRY_DELAY : Duration = Duration :: from_secs ( 30 ) ;
3538
3639pub fn run ( ) -> Result < ( ) , windows_service:: Error > {
3740 // Register generated `ffi_service_main` with the system and start the service, blocking
@@ -112,25 +115,69 @@ fn run_service() -> Result<(), DaemonError> {
112115
113116 let service_location_manager = Arc :: new ( RwLock :: new ( service_location_manager) ) ;
114117
115- // Spawn service location management task
118+ // Spawn network change monitoring task first so NotifyAddrChange is registered as early
119+ // as possible, minimising the window in which a network event could be missed before
120+ // the watcher is listening. The retry task below is the backstop for any event that
121+ // still slips through that window.
116122 let service_location_manager_clone = service_location_manager. clone ( ) ;
117123 runtime. spawn ( async move {
118124 let manager = service_location_manager_clone;
125+ info ! ( "Starting network change monitoring" ) ;
126+ watch_for_network_change ( manager. clone ( ) ) . await ;
127+ error ! ( "Network change monitoring ended unexpectedly." ) ;
128+ } ) ;
119129
120- info ! ( "Starting service location management task" ) ;
121-
122- info ! ( "Attempting to auto-connect to service locations" ) ;
123- match manager. write ( ) . unwrap ( ) . connect_to_service_locations ( ) {
124- Ok ( ( ) ) => {
125- info ! ( "Auto-connect to service locations completed successfully" ) ;
130+ // Spawn service location auto-connect task with retries.
131+ // Each attempt skips locations that are already connected, so it is safe to call
132+ // connect_to_service_locations repeatedly. The retry loop exists to handle the case
133+ // where the connection may fail initially at startup because the network
134+ // (e.g. Wi-Fi) is not yet available (mainly DNS resolution issues), and serves as
135+ // a backstop for any network events missed by the watcher above.
136+ // If all locations connect successfully on a given attempt, no further retries are made.
137+ let service_location_manager_connect = service_location_manager. clone ( ) ;
138+ runtime. spawn ( async move {
139+ for attempt in 1 ..=SERVICE_LOCATION_CONNECT_RETRY_COUNT {
140+ info ! (
141+ "Attempting to auto-connect to service locations \
142+ (attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT})"
143+ ) ;
144+ match service_location_manager_connect
145+ . write ( )
146+ . unwrap ( )
147+ . connect_to_service_locations ( )
148+ {
149+ Ok ( true ) => {
150+ info ! (
151+ "All service locations connected successfully \
152+ (attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT})"
153+ ) ;
154+ break ;
155+ }
156+ Ok ( false ) => {
157+ warn ! (
158+ "Auto-connect attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT} \
159+ completed with some failures"
160+ ) ;
161+ }
162+ Err ( err) => {
163+ warn ! (
164+ "Auto-connect attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT} \
165+ failed: {err}"
166+ ) ;
167+ }
126168 }
127- Err ( err) => {
128- warn ! (
129- "Error while trying to auto-connect to service locations: {err}. \
130- Will continue monitoring for login/logoff events.",
131- ) ;
169+
170+ if attempt < SERVICE_LOCATION_CONNECT_RETRY_COUNT {
171+ sleep ( SERVICE_LOCATION_CONNECT_RETRY_DELAY ) . await ;
132172 }
133173 }
174+ info ! ( "Service location auto-connect task finished" ) ;
175+ } ) ;
176+
177+ // Spawn login/logoff monitoring task, runs concurrently with the tasks above.
178+ let service_location_manager_clone = service_location_manager. clone ( ) ;
179+ runtime. spawn ( async move {
180+ let manager = service_location_manager_clone;
134181
135182 info ! ( "Starting login/logoff event monitoring" ) ;
136183 loop {
@@ -140,14 +187,14 @@ fn run_service() -> Result<(), DaemonError> {
140187 "Login/logoff event monitoring ended unexpectedly. Restarting in \
141188 {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}..."
142189 ) ;
143- tokio :: time :: sleep ( LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS ) . await ;
190+ sleep ( LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS ) . await ;
144191 }
145192 Err ( e) => {
146193 error ! (
147194 "Error in login/logoff event monitoring: {e}. Restarting in \
148195 {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}...",
149196 ) ;
150- tokio :: time :: sleep ( LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS ) . await ;
197+ sleep ( LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS ) . await ;
151198 info ! ( "Restarting login/logoff event monitoring" ) ;
152199 }
153200 }
0 commit comments