diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-08-26 10:42:38 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-08-26 10:42:38 +0200 |
| commit | 1499e2ab919b853e714cb18a323b77db640e0a51 (patch) | |
| tree | 6104e6bd86cfff5875833f71589964a7a7165b49 | |
| parent | 32e61accd7e01ffe08cdcf2fc743aa36346d3b43 (diff) | |
| parent | 48938553008cb32fd1b5358f5f47ec82865dab27 (diff) | |
| download | mullvadvpn-1499e2ab919b853e714cb18a323b77db640e0a51.tar.xz mullvadvpn-1499e2ab919b853e714cb18a323b77db640e0a51.zip | |
Merge branch 'install-st-on-demand'
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | dist-assets/windows/installer.nsh | 157 | ||||
| -rw-r--r-- | gui/tasks/distribution.js | 1 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 3 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/driver.rs | 15 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/mod.rs | 58 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/service.rs | 205 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 1 | ||||
| -rw-r--r-- | windows/driverlogic/driverlogic.vcxproj | 2 | ||||
| -rw-r--r-- | windows/driverlogic/driverlogic.vcxproj.filters | 2 | ||||
| -rw-r--r-- | windows/driverlogic/src/device.cpp | 170 | ||||
| -rw-r--r-- | windows/driverlogic/src/device.h | 7 | ||||
| -rw-r--r-- | windows/driverlogic/src/driverlogic.cpp | 115 | ||||
| -rw-r--r-- | windows/driverlogic/src/service.cpp | 96 | ||||
| -rw-r--r-- | windows/driverlogic/src/service.h | 2 | ||||
| -rw-r--r-- | windows/driverlogic/src/version.cpp | 125 | ||||
| -rw-r--r-- | windows/driverlogic/src/version.h | 30 |
17 files changed, 394 insertions, 596 deletions
diff --git a/Cargo.lock b/Cargo.lock index 3be1fd40c4..ae54ff53cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3120,6 +3120,7 @@ dependencies = [ "which", "widestring 0.5.1", "winapi", + "windows-service", "windows-sys", "winreg", "zeroize", diff --git a/dist-assets/windows/installer.nsh b/dist-assets/windows/installer.nsh index 7b1f552e2e..444846a3a0 100644 --- a/dist-assets/windows/installer.nsh +++ b/dist-assets/windows/installer.nsh @@ -23,10 +23,6 @@ # Return codes from driverlogic !define DL_GENERAL_SUCCESS 0 !define DL_GENERAL_ERROR 1 -!define DL_ST_DRIVER_NONE_INSTALLED 2 -!define DL_ST_DRIVER_SAME_VERSION_INSTALLED 3 -!define DL_ST_DRIVER_OLDER_VERSION_INSTALLED 4 -!define DL_ST_DRIVER_NEWER_VERSION_INSTALLED 5 # Log targets !define LOG_INSTALL 0 @@ -74,7 +70,6 @@ # The working directory cannot be deleted, so make sure it's set to $TEMP. SetOutPath "$TEMP" - RMDir /r "$TEMP\mullvad-split-tunnel" Delete "$TEMP\wintun.dll" Delete "$TEMP\mullvad-wireguard.dll" Delete "$TEMP\driverlogic.exe" @@ -88,6 +83,20 @@ !define CleanupTempFiles '!insertmacro "CleanupTempFiles"' # +# ExtractDriverlogic +# +# Extract device setup tools to $TEMP +# +!macro ExtractDriverlogic + + SetOutPath "$TEMP" + File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\x64-$%CPP_BUILD_MODE%\driverlogic.exe" + +!macroend + +!define ExtractDriverlogic '!insertmacro "ExtractDriverlogic"' + +# # ExtractWireGuard # # Extract Wintun and WireGuardNT installer into $TEMP @@ -97,7 +106,6 @@ SetOutPath "$TEMP" File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\wintun\wintun.dll" File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\wireguard-nt\mullvad-wireguard.dll" - File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\x64-$%CPP_BUILD_MODE%\driverlogic.exe" !macroend @@ -119,23 +127,6 @@ !define ExtractMullvadSetup '!insertmacro "ExtractMullvadSetup"' # -# ExtractSplitTunnelDriver -# -# Extract split tunnel driver and associated files into $TEMP\mullvad-split-tunnel -# -!macro ExtractSplitTunnelDriver - - SetOutPath "$TEMP\mullvad-split-tunnel" - File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\split-tunnel\mullvad-split-tunnel.cat" - File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\split-tunnel\mullvad-split-tunnel.inf" - File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\split-tunnel\mullvad-split-tunnel.sys" - File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\x64-$%CPP_BUILD_MODE%\driverlogic.exe" - -!macroend - -!define ExtractSplitTunnelDriver '!insertmacro "ExtractSplitTunnelDriver"' - -# # RemoveWintun # # Try to remove Wintun @@ -314,97 +305,6 @@ !define InstallService '!insertmacro "InstallService"' # -# InstallSplitTunnelDriver -# -# Install split tunnel driver -# -# Returns: 0 in $R0 on success, otherwise an error message in $R0 -# -!macro InstallSplitTunnelDriver - - log::Log "InstallSplitTunnelDriver()" - - Push $0 - Push $1 - - log::Log "Searching for and evaluating already installed Split Tunneling driver" - nsExec::ExecToStack '"$TEMP\mullvad-split-tunnel\driverlogic.exe" st-evaluate "$TEMP\mullvad-split-tunnel\mullvad-split-tunnel.inf"' - - Pop $0 - Pop $1 - - ${If} $0 == ${DL_ST_DRIVER_NONE_INSTALLED} - log::Log "No currently installed Split Tunneling driver" - Goto InstallSplitTunnelDriver_new_install - ${OrIf} $0 == ${DL_ST_DRIVER_SAME_VERSION_INSTALLED} - log::Log "Up-to-date Split Tunneling driver already installed" - Goto InstallSplitTunnelDriver_success - ${OrIf} $0 == ${DL_ST_DRIVER_OLDER_VERSION_INSTALLED} - log::Log "An older version of the Split Tunneling driver is installed" - Goto InstallSplitTunnelDriver_force_install - ${OrIf} $0 == ${DL_ST_DRIVER_NEWER_VERSION_INSTALLED} - log::Log "A newer version of the Split Tunneling driver is installed" - Goto InstallSplitTunnelDriver_force_install - ${Else} - IntFmt $0 "0x%X" $0 - StrCpy $R0 "Failed to search for and evaluate driver: error $0" - log::LogWithDetails $R0 $1 - Goto InstallSplitTunnelDriver_return - ${EndIf} - - InstallSplitTunnelDriver_new_install: - - log::Log "Installing Split Tunneling driver" - nsExec::ExecToStack '"$TEMP\mullvad-split-tunnel\driverlogic.exe" st-new-install "$TEMP\mullvad-split-tunnel\mullvad-split-tunnel.inf"' - - Pop $0 - Pop $1 - - ${If} $0 != ${DL_GENERAL_SUCCESS} - IntFmt $0 "0x%X" $0 - StrCpy $R0 "Failed to install driver: error $0" - log::LogWithDetails $R0 $1 - Goto InstallSplitTunnelDriver_return - ${EndIf} - - Goto InstallSplitTunnelDriver_success - - InstallSplitTunnelDriver_force_install: - - # - # Would be possible to check driver state here and warn the user if driver is engaged. - # - - log::Log "Installing Split Tunneling driver" - nsExec::ExecToStack '"$TEMP\mullvad-split-tunnel\driverlogic.exe" st-force-install "$TEMP\mullvad-split-tunnel\mullvad-split-tunnel.inf"' - - Pop $0 - Pop $1 - - ${If} $0 != ${DL_GENERAL_SUCCESS} - IntFmt $0 "0x%X" $0 - StrCpy $R0 "Failed to install driver: error $0" - log::LogWithDetails $R0 $1 - Goto InstallSplitTunnelDriver_return - ${EndIf} - - InstallSplitTunnelDriver_success: - - log::Log "InstallSplitTunnelDriver() completed successfully" - - Push 0 - Pop $R0 - - InstallSplitTunnelDriver_return: - - Pop $1 - Pop $0 - -!macroend - -!define InstallSplitTunnelDriver '!insertmacro "InstallSplitTunnelDriver"' - -# # RemoveSplitTunnelDriver # # Reset and remove split tunnel driver @@ -417,7 +317,7 @@ Push $1 log::Log "Removing Split Tunneling driver" - nsExec::ExecToStack '"$TEMP\mullvad-split-tunnel\driverlogic.exe" st-remove' + nsExec::ExecToStack '"$TEMP\driverlogic.exe" st-remove' Pop $0 Pop $1 @@ -893,18 +793,21 @@ ${RemoveRelayCache} ${RemoveApiAddressCache} - SetOutPath "$TEMP" - File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\x64-$%CPP_BUILD_MODE%\driverlogic.exe" + ${ExtractDriverlogic} ${RemoveAbandonedWintunAdapter} - ${ExtractSplitTunnelDriver} - ${InstallSplitTunnelDriver} + ${If} $R0 != 0 + MessageBox MB_OK "$R0" + Goto customInstall_abort_installation + ${EndIf} + + ${RemoveSplitTunnelDriver} ${If} $R0 != 0 MessageBox MB_OK "$R0" Goto customInstall_abort_installation ${EndIf} - + ${InstallService} ${If} $R0 != 0 @@ -1133,6 +1036,7 @@ Pop $Silent + ${ExtractDriverlogic} ${ExtractMullvadSetup} ${If} $Silent == 1 @@ -1179,6 +1083,16 @@ Goto customRemoveFiles_abort ${EndIf} + # Precaution: If the daemon fails to exit gracefully, + # attempt to remove the driver here. Otherwise, the + # installer may fail to delete the install dir. + + ${RemoveSplitTunnelDriver} + + ${If} $R0 != 0 + Goto customRemoveFiles_abort + ${EndIf} + # Remove application files log::Log "Deleting $INSTDIR" RMDir /r $INSTDIR @@ -1212,9 +1126,6 @@ ${RemoveWintun} ${RemoveWireGuardNt} - ${ExtractSplitTunnelDriver} - ${RemoveSplitTunnelDriver} - log::SetLogTarget ${LOG_VOID} ${RemoveLogsAndCache} diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index 8fde833d4f..75e0f246cc 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -146,6 +146,7 @@ const config = { { from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' }, { from: root('build/lib/x86_64-pc-windows-msvc/libwg.dll'), to: '.' }, { from: distAssets('binaries/x86_64-pc-windows-msvc/wintun/wintun.dll'), to: '.' }, + { from: distAssets('binaries/x86_64-pc-windows-msvc/split-tunnel/mullvad-split-tunnel.sys'), to: '.' }, { from: distAssets('binaries/x86_64-pc-windows-msvc/wireguard-nt/mullvad-wireguard.dll'), to: '.', diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 6216a23bb2..d36460cf41 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -84,12 +84,14 @@ winreg = { version = "0.7", features = ["transactions"] } winapi = { version = "0.3.6", features = ["ws2def"] } talpid-platform-metadata = { path = "../talpid-platform-metadata" } memoffset = "0.6" +windows-service = "0.5.0" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.36.1" features = [ "Win32_Foundation", "Win32_Globalization", + "Win32_Security", "Win32_System_Com", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Ioctl", @@ -97,6 +99,7 @@ features = [ "Win32_System_LibraryLoader", "Win32_System_ProcessStatus", "Win32_System_Registry", + "Win32_System_Services", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming", diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs index f73e170cce..a3eb338c8c 100644 --- a/talpid-core/src/split_tunnel/windows/driver.rs +++ b/talpid-core/src/split_tunnel/windows/driver.rs @@ -214,8 +214,14 @@ pub enum DeviceHandleError { impl DeviceHandle { pub fn new() -> Result<Self, DeviceHandleError> { - // Connect to the driver + let device = Self::new_handle_only()?; + device.reinitialize()?; + Ok(device) + } + + pub(super) fn new_handle_only() -> Result<Self, DeviceHandleError> { log::trace!("Connecting to the driver"); + let handle = OpenOptions::new() .read(true) .write(true) @@ -228,10 +234,7 @@ impl DeviceHandle { Some(ERROR_ACCESS_DENIED) => DeviceHandleError::ConnectionDenied, _ => DeviceHandleError::ConnectionError(e), })?; - - let device = Self { handle }; - device.reinitialize()?; - Ok(device) + Ok(Self { handle }) } pub fn reinitialize(&self) -> Result<(), DeviceHandleError> { @@ -385,7 +388,7 @@ impl DeviceHandle { Ok(()) } - fn reset(&self) -> io::Result<()> { + pub(super) fn reset(&self) -> io::Result<()> { device_io_control(self, DriverIoctlCode::Reset as u32, None, 0)?; Ok(()) } diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs index 47c7d4ec8f..ed5102ab45 100644 --- a/talpid-core/src/split_tunnel/windows/mod.rs +++ b/talpid-core/src/split_tunnel/windows/mod.rs @@ -1,5 +1,6 @@ mod driver; mod path_monitor; +mod service; mod volume_monitor; mod windows; @@ -39,10 +40,18 @@ const RESERVED_IP_V4: Ipv4Addr = Ipv4Addr::new(192, 0, 2, 123); #[derive(err_derive::Error, Debug)] #[error(no_from)] pub enum Error { + /// Failed to install or start driver service + #[error(display = "Failed to start driver service")] + ServiceError(#[error(source)] service::Error), + /// Failed to initialize the driver #[error(display = "Failed to initialize driver")] InitializationError(#[error(source)] driver::DeviceHandleError), + /// Failed to reset the driver + #[error(display = "Failed to reset driver")] + ResetError(#[error(source)] io::Error), + /// Failed to set paths to excluded applications #[error(display = "Failed to set list of excluded applications")] SetConfiguration(#[error(source)] io::Error), @@ -117,6 +126,7 @@ enum Request { SetPaths(Vec<OsString>), RegisterIps(InterfaceAddresses), Restart, + Stop, } type RequestResponseTx = sync_mpsc::Sender<Result<(), Error>>; type RequestTx = sync_mpsc::Sender<(Request, RequestResponseTx)>; @@ -173,6 +183,7 @@ impl SplitTunnel { /// Initialize the split tunnel device. pub fn new( runtime: tokio::runtime::Handle, + resource_dir: PathBuf, daemon_tx: Weak<mpsc::UnboundedSender<TunnelCommand>>, volume_update_rx: mpsc::UnboundedReceiver<()>, power_mgmt_rx: PowerManagementListener, @@ -180,7 +191,7 @@ impl SplitTunnel { let excluded_processes = Arc::new(RwLock::new(HashMap::new())); let (request_tx, handle) = - Self::spawn_request_thread(volume_update_rx, excluded_processes.clone())?; + Self::spawn_request_thread(resource_dir, volume_update_rx, excluded_processes.clone())?; let (event_thread, quit_event) = Self::spawn_event_listener(handle, excluded_processes.clone())?; @@ -400,6 +411,7 @@ impl SplitTunnel { } fn spawn_request_thread( + resource_dir: PathBuf, volume_update_rx: mpsc::UnboundedReceiver<()>, excluded_processes: Arc<RwLock<HashMap<usize, ExcludedProcess>>>, ) -> Result<(RequestTx, Arc<driver::DeviceHandle>), Error> { @@ -421,10 +433,14 @@ impl SplitTunnel { ); std::thread::spawn(move || { - let result = driver::DeviceHandle::new() - .map(Arc::new) - .map_err(Error::InitializationError); - let handle = match result { + let init_fn = || { + service::install_driver_if_required(&resource_dir).map_err(Error::ServiceError)?; + driver::DeviceHandle::new() + .map(Arc::new) + .map_err(Error::InitializationError) + }; + + let handle = match init_fn() { Ok(handle) => { let _ = init_tx.send(Ok(handle.clone())); handle @@ -518,6 +534,20 @@ impl SplitTunnel { Ok(()) })() } + Request::Stop => { + if let Err(error) = handle.reset().map_err(Error::ResetError) { + let _ = response_tx.send(Err(error)); + continue; + } + + monitored_paths.lock().unwrap().clear(); + excluded_processes.write().unwrap().clear(); + + let _ = response_tx.send(Ok(())); + + // Stop listening to commands + break; + } }; if response_tx.send(response).is_err() { log::error!("A response could not be sent for a completed request"); @@ -531,6 +561,16 @@ impl SplitTunnel { error.display_chain_with_msg("Failed to shut down path monitor") ); } + + drop(handle); + + log::debug!("Stopping ST service"); + if let Err(error) = service::stop_driver_service() { + log::error!( + "{}", + error.display_chain_with_msg("Failed to stop ST service") + ); + } }); let handle = init_rx @@ -718,9 +758,11 @@ impl Drop for SplitTunnel { // Not joining `event_thread`: It may be unresponsive. } - let paths: [&OsStr; 0] = []; - if let Err(error) = self.set_paths_sync(&paths) { - log::error!("{}", error.display_chain()); + if let Err(error) = self.send_request(Request::Stop) { + log::error!( + "{}", + error.display_chain_with_msg("Failed to stop ST driver service") + ); } } } diff --git a/talpid-core/src/split_tunnel/windows/service.rs b/talpid-core/src/split_tunnel/windows/service.rs new file mode 100644 index 0000000000..e71e9abc1d --- /dev/null +++ b/talpid-core/src/split_tunnel/windows/service.rs @@ -0,0 +1,205 @@ +use std::{ + ffi::{OsStr, OsString}, + io, + path::{Path, PathBuf}, + time::Duration, +}; +use windows_service::{ + service::{ + Service, ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, + ServiceType, + }, + service_manager::{ServiceManager, ServiceManagerAccess}, +}; +use windows_sys::Win32::Foundation::{ERROR_SERVICE_ALREADY_RUNNING, ERROR_SERVICE_DOES_NOT_EXIST}; + +const SPLIT_TUNNEL_SERVICE: &str = "mullvad-split-tunnel"; +const SPLIT_TUNNEL_DISPLAY_NAME: &str = "Mullvad Split Tunnel Service"; +const DRIVER_FILENAME: &str = "mullvad-split-tunnel.sys"; + +const WAIT_STATUS_TIMEOUT: Duration = Duration::from_secs(8); + +#[derive(err_derive::Error, Debug)] +#[error(no_from)] +pub enum Error { + /// Failed to open service control manager + #[error(display = "Failed to connect to service control manager")] + OpenServiceControlManager(#[error(source)] windows_service::Error), + + /// Failed to create a service handle + #[error(display = "Failed to open service")] + OpenServiceHandle(#[error(source)] windows_service::Error), + + /// Failed to start split tunnel service + #[error(display = "Failed to start split tunnel device driver service")] + StartService(#[error(source)] windows_service::Error), + + /// Failed to check service status + #[error(display = "Failed to query service status")] + QueryServiceStatus(#[error(source)] windows_service::Error), + + /// Failed to open service config + #[error(display = "Failed to retrieve service config")] + QueryServiceConfig(#[error(source)] windows_service::Error), + + /// Failed to install ST service + #[error(display = "Failed to install split tunnel driver")] + InstallService(#[error(source)] windows_service::Error), + + /// Failed to start ST service + #[error(display = "Timed out waiting on service to start")] + StartTimeout, + + /// Failed to connect to existing driver + #[error(display = "Failed to open service handle")] + OpenHandle(#[error(source)] super::driver::DeviceHandleError), + + /// Failed to reset existing driver + #[error(display = "Failed to reset driver state")] + ResetDriver(#[error(source)] io::Error), +} + +pub fn install_driver_if_required(resource_dir: &Path) -> Result<(), Error> { + let scm = ServiceManager::local_computer( + None::<OsString>, + ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE, + ) + .map_err(Error::OpenServiceControlManager)?; + + let expected_syspath = resource_dir.join(DRIVER_FILENAME); + + let service = match scm.open_service(SPLIT_TUNNEL_SERVICE, ServiceAccess::all()) { + Ok(service) => service, + Err(error) => { + return match error { + windows_service::Error::Winapi(io_error) + if io_error.raw_os_error() == Some(ERROR_SERVICE_DOES_NOT_EXIST as i32) => + { + // TODO: could be marked for deletion + install_driver(&scm, &expected_syspath) + } + error => Err(Error::OpenServiceHandle(error)), + }; + } + }; + + if expected_syspath != get_driver_binpath(&service)? { + log::debug!("ST driver is already installed"); + return start_and_wait_for_service(&service); + } + + log::debug!("Replacing ST driver due to unexpected path"); + + remove_device(service)?; + install_driver(&scm, &expected_syspath) +} + +pub fn stop_driver_service() -> Result<(), Error> { + let scm = ServiceManager::local_computer(None::<OsString>, ServiceManagerAccess::CONNECT) + .map_err(Error::OpenServiceControlManager)?; + + let service = match scm.open_service(SPLIT_TUNNEL_SERVICE, ServiceAccess::all()) { + Ok(service) => service, + Err(error) => { + return match error { + windows_service::Error::Winapi(io_error) + if io_error.raw_os_error() == Some(ERROR_SERVICE_DOES_NOT_EXIST as i32) => + { + Ok(()) + } + error => Err(Error::OpenServiceHandle(error)), + }; + } + }; + + stop_service(&service) +} + +fn stop_service(service: &Service) -> Result<(), Error> { + let _ = service.stop(); + wait_for_status(service, ServiceState::Stopped) +} + +fn remove_device(service: Service) -> Result<(), Error> { + reset_driver(&service)?; + stop_service(&service)?; + let _ = service.delete(); + Ok(()) +} + +fn reset_driver(service: &Service) -> Result<(), Error> { + let status = service.query_status().map_err(Error::QueryServiceStatus)?; + + if status.current_state == ServiceState::Running { + let old_handle = + super::driver::DeviceHandle::new_handle_only().map_err(Error::OpenHandle)?; + old_handle.reset().map_err(Error::ResetDriver)?; + } + + Ok(()) +} + +fn install_driver(scm: &ServiceManager, syspath: &Path) -> Result<(), Error> { + log::debug!("Installing split tunnel driver"); + + let service_info = ServiceInfo { + name: SPLIT_TUNNEL_SERVICE.into(), + display_name: SPLIT_TUNNEL_DISPLAY_NAME.into(), + service_type: ServiceType::KERNEL_DRIVER, + start_type: ServiceStartType::OnDemand, + error_control: ServiceErrorControl::Normal, + executable_path: syspath.to_path_buf(), + launch_arguments: vec![], + dependencies: vec![], + account_name: None, + account_password: None, + }; + + let service = scm + .create_service( + &service_info, + ServiceAccess::START | ServiceAccess::QUERY_STATUS, + ) + .map_err(Error::InstallService)?; + + start_and_wait_for_service(&service) +} + +fn start_and_wait_for_service(service: &Service) -> Result<(), Error> { + log::debug!("Starting split tunnel service"); + + if let Err(error) = service.start::<&OsStr>(&[]) { + if let windows_service::Error::Winapi(error) = &error { + if error.raw_os_error() == Some(ERROR_SERVICE_ALREADY_RUNNING as i32) { + return Ok(()); + } + } + return Err(Error::StartService(error)); + } + + wait_for_status(service, ServiceState::Running) +} + +fn wait_for_status(service: &Service, target_state: ServiceState) -> Result<(), Error> { + let initial_time = std::time::Instant::now(); + loop { + let status = service.query_status().map_err(Error::QueryServiceStatus)?; + + if status.current_state == target_state { + break; + } + + if initial_time.elapsed() >= WAIT_STATUS_TIMEOUT { + return Err(Error::StartTimeout); + } + + std::thread::sleep(std::time::Duration::from_secs(1)); + } + + Ok(()) +} + +fn get_driver_binpath(service: &Service) -> Result<PathBuf, Error> { + let config = service.query_config().map_err(Error::QueryServiceConfig)?; + Ok(config.executable_path) +} diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 061798b1e2..c1b52278f0 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -261,6 +261,7 @@ impl TunnelStateMachine { #[cfg(windows)] let split_tunnel = split_tunnel::SplitTunnel::new( runtime.clone(), + args.resource_dir.clone(), args.command_tx.clone(), volume_update_rx, power_mgmt_rx.clone(), diff --git a/windows/driverlogic/driverlogic.vcxproj b/windows/driverlogic/driverlogic.vcxproj index cc46c1ac72..3172356a60 100644 --- a/windows/driverlogic/driverlogic.vcxproj +++ b/windows/driverlogic/driverlogic.vcxproj @@ -104,7 +104,6 @@ <ClCompile Include="src\service.cpp" /> <ClCompile Include="src\stdafx.cpp" /> <ClCompile Include="src\util.cpp" /> - <ClCompile Include="src\version.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="src\devenum.h" /> @@ -115,7 +114,6 @@ <ClInclude Include="src\stdafx.h" /> <ClInclude Include="src\targetver.h" /> <ClInclude Include="src\util.h" /> - <ClInclude Include="src\version.h" /> <ClInclude Include="src\wintun.h" /> <ClInclude Include="src\wireguard.h" /> </ItemGroup> diff --git a/windows/driverlogic/driverlogic.vcxproj.filters b/windows/driverlogic/driverlogic.vcxproj.filters index 9665231376..bd73d30029 100644 --- a/windows/driverlogic/driverlogic.vcxproj.filters +++ b/windows/driverlogic/driverlogic.vcxproj.filters @@ -13,7 +13,6 @@ <ClCompile Include="src\device.cpp" /> <ClCompile Include="src\service.cpp" /> <ClCompile Include="src\log.cpp" /> - <ClCompile Include="src\version.cpp" /> <ClCompile Include="src\util.cpp" /> <ClCompile Include="src\devenum.cpp" /> </ItemGroup> @@ -24,7 +23,6 @@ <ClInclude Include="src\device.h" /> <ClInclude Include="src\service.h" /> <ClInclude Include="src\log.h" /> - <ClInclude Include="src\version.h" /> <ClInclude Include="src\util.h" /> <ClInclude Include="src\wintun.h" /> <ClInclude Include="src\devenum.h" /> diff --git a/windows/driverlogic/src/device.cpp b/windows/driverlogic/src/device.cpp index e38709f01d..71abb56266 100644 --- a/windows/driverlogic/src/device.cpp +++ b/windows/driverlogic/src/device.cpp @@ -38,47 +38,6 @@ constexpr SIZE_T ST_DRIVER_STATE_STARTED = 1; // Onwards. // -void -ThrowUpdateException -( - DWORD lastError, - const char *operation -) -{ - if (ERROR_DEVICE_INSTALLER_NOT_READY == lastError) - { - bool deviceInstallDisabled = false; - - try - { - const auto key = common::registry::Registry::OpenKey - ( - HKEY_LOCAL_MACHINE, - L"SYSTEM\\CurrentControlSet\\Services\\DeviceInstall\\Parameters" - ); - - deviceInstallDisabled = (0 != key->readUint32(L"DeviceInstallDisabled")); - } - catch (...) - { - } - - if (deviceInstallDisabled) - { - throw common::error::WindowsException - ( - "Device installs must be enabled to continue. " - "Enable them in the Local Group Policy editor, or " - "update the registry value DeviceInstallDisabled in " - "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DeviceInstall\\Parameters]", - lastError - ); - } - } - - THROW_SETUPAPI_ERROR(lastError, operation); -} - } // anonymous namespace std::wstring @@ -196,135 +155,6 @@ GetDeviceNetCfgInstanceId } void -CreateDevice -( - const GUID &classGuid, - const std::wstring &deviceName, - const std::wstring &deviceHardwareId -) -{ - Log(L"Attempting to create device"); - - const auto deviceInfoSet = SetupDiCreateDeviceInfoList(&classGuid, 0); - - if (INVALID_HANDLE_VALUE == deviceInfoSet) - { - THROW_SETUPAPI_ERROR(GetLastError(), "SetupDiCreateDeviceInfoList"); - } - - common::memory::ScopeDestructor scopeDestructor; - - scopeDestructor += [&deviceInfoSet]() - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - }; - - SP_DEVINFO_DATA devInfoData {0}; - devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - auto status = SetupDiCreateDeviceInfoW - ( - deviceInfoSet, - deviceName.c_str(), - &classGuid, - nullptr, - 0, - DICD_GENERATE_ID, - &devInfoData - ); - - if (FALSE == status) - { - THROW_SETUPAPI_ERROR(GetLastError(), "SetupDiCreateDeviceInfoW"); - } - - status = SetupDiSetDeviceRegistryPropertyW - ( - deviceInfoSet, - &devInfoData, - SPDRP_HARDWAREID, - reinterpret_cast<const BYTE *>(deviceHardwareId.c_str()), - static_cast<DWORD>(deviceHardwareId.size() * sizeof(wchar_t)) - ); - - if (FALSE == status) - { - THROW_SETUPAPI_ERROR(GetLastError(), "SetupDiSetDeviceRegistryPropertyW"); - } - - // - // Create a devnode in the PnP HW tree - // - status = SetupDiCallClassInstaller - ( - DIF_REGISTERDEVICE, - deviceInfoSet, - &devInfoData - ); - - if (FALSE == status) - { - THROW_SETUPAPI_ERROR(GetLastError(), "SetupDiCallClassInstaller"); - } - - Log(L"Created new device successfully"); -} - -void -InstallDriverForDevice -( - const std::wstring &deviceHardwareId, - const std::wstring &infPath -) -{ - Log(L"Attempting to install new driver"); - - DWORD installFlags = 0; - BOOL rebootRequired = FALSE; - - for (;;) - { - auto result = UpdateDriverForPlugAndPlayDevicesW - ( - nullptr, - deviceHardwareId.c_str(), - infPath.c_str(), - installFlags, - &rebootRequired - ); - - if (FALSE != result) - { - break; - } - - const auto lastError = GetLastError(); - - if (ERROR_NO_MORE_ITEMS == lastError - && (0 == (installFlags & INSTALLFLAG_FORCE))) - { - Log(L"Driver installation/update failed. Attempting forced install."); - installFlags |= INSTALLFLAG_FORCE; - - continue; - } - - ThrowUpdateException(lastError, "UpdateDriverForPlugAndPlayDevicesW"); - } - - // - // Driver successfully installed or updated - // - - std::wstringstream ss; - - ss << L"Device driver update complete. Reboot required: " - << rebootRequired; - - Log(ss.str()); -} - -void UninstallDevice ( const EnumeratedDevice &device diff --git a/windows/driverlogic/src/device.h b/windows/driverlogic/src/device.h index b8bc0e7f41..4dc8c4500d 100644 --- a/windows/driverlogic/src/device.h +++ b/windows/driverlogic/src/device.h @@ -39,13 +39,6 @@ CreateDevice ); void -InstallDriverForDevice -( - const std::wstring &deviceHardwareId, - const std::wstring &infPath -); - -void UninstallDevice ( const EnumeratedDevice &device diff --git a/windows/driverlogic/src/driverlogic.cpp b/windows/driverlogic/src/driverlogic.cpp index 2fdcc3046c..895272fa4f 100644 --- a/windows/driverlogic/src/driverlogic.cpp +++ b/windows/driverlogic/src/driverlogic.cpp @@ -3,7 +3,6 @@ #include "device.h" #include "service.h" #include "log.h" -#include "version.h" #include "wintun.h" #include "wireguard.h" #include "devenum.h" @@ -20,8 +19,6 @@ namespace { -constexpr wchar_t SPLIT_TUNNEL_HARDWARE_ID[] = L"Root\\mullvad-split-tunnel"; - DEFINE_GUID(WFP_CALLOUTS_CLASS_ID, 0x57465043, 0x616C, 0x6C6F, 0x75, 0x74, 0x5F, 0x63, 0x6C, 0x61, 0x73, 0x73); @@ -30,11 +27,7 @@ constexpr wchar_t SPLIT_TUNNEL_DEVICE_NAME[] = L"Mullvad Split Tunnel Device"; enum ReturnCode { GENERAL_SUCCESS = 0, - GENERAL_ERROR = 1, - ST_DRIVER_NONE_INSTALLED = 2, - ST_DRIVER_SAME_VERSION_INSTALLED = 3, - ST_DRIVER_OLDER_VERSION_INSTALLED = 4, - ST_DRIVER_NEWER_VERSION_INSTALLED = 5 + GENERAL_ERROR = 1 }; class ArgumentContext @@ -105,74 +98,6 @@ std::unique_ptr<DeviceEnumerator> CreateSplitTunnelDeviceEnumerator() } // -// CommandSplitTunnelEvaluate() -// -// Search for existing device. -// Evaluate if provided inf can/should be installed. -// -ReturnCode CommandSplitTunnelEvaluate(const std::vector<std::wstring> &args) -{ - ArgumentContext argsContext(args); - - argsContext.ensureExactArgumentCount(1); - - const auto infPath = argsContext.next(); - - // - // Find first matching device - // - - auto enumerator = CreateSplitTunnelDeviceEnumerator(); - - EnumeratedDevice device; - - if (!enumerator->next(device)) - { - return ReturnCode::ST_DRIVER_NONE_INSTALLED; - } - - // - // Retrieve driver versions - // - - auto existingVersion = GetDriverVersion(device); - auto proposedVersion = InfGetDriverVersion(infPath); - - // - // Compare driver versions - // - - switch (EvaluateDriverUpgrade(existingVersion, proposedVersion)) - { - case DRIVER_UPGRADE_STATUS::WOULD_UPGRADE: - return ReturnCode::ST_DRIVER_OLDER_VERSION_INSTALLED; - case DRIVER_UPGRADE_STATUS::WOULD_DOWNGRADE: - return ReturnCode::ST_DRIVER_NEWER_VERSION_INSTALLED; - case DRIVER_UPGRADE_STATUS::WOULD_INSTALL_SAME_VERSION: - return ReturnCode::ST_DRIVER_SAME_VERSION_INSTALLED; - default: - Log(L"Unexpected return value from EvaluateDriverUpgrade()"); - } - - return ReturnCode::GENERAL_ERROR; -} - -ReturnCode CommandSplitTunnelNewInstall(const std::vector<std::wstring> &args) -{ - ArgumentContext argsContext(args); - - argsContext.ensureExactArgumentCount(1); - - const auto infPath = argsContext.next(); - - CreateDevice(WFP_CALLOUTS_CLASS_ID, SPLIT_TUNNEL_DEVICE_NAME, SPLIT_TUNNEL_HARDWARE_ID); - - InstallDriverForDevice(SPLIT_TUNNEL_HARDWARE_ID, infPath); - - return ReturnCode::GENERAL_SUCCESS; -} - -// // CommandSplitTunnelRemove() // // Reset driver @@ -186,48 +111,29 @@ ReturnCode CommandSplitTunnelRemove(const std::vector<std::wstring> &args) argsContext.ensureExactArgumentCount(0); + if (ServiceIsRunning(L"mullvad-split-tunnel")) + { + ResetDriverState(); + } + // - // Find first matching device + // Uninstall device, if it exists // auto enumerator = CreateSplitTunnelDeviceEnumerator(); EnumeratedDevice device; - if (!enumerator->next(device)) + if (enumerator->next(device)) { - Log(L"Could not find split tunnel device"); - - return ReturnCode::GENERAL_SUCCESS; + UninstallDevice(device); } - ResetDriverState(); - - UninstallDevice(device); - PokeService(L"mullvad-split-tunnel", true, true); return ReturnCode::GENERAL_SUCCESS; } -// -// CommandSplitTunnelForceInstall() -// -// There's an existing device that needs to be stopped and removed. -// After this, create a new device and associate the specified inf. -// -ReturnCode CommandSplitTunnelForceInstall(const std::vector<std::wstring> &args) -{ - auto status = CommandSplitTunnelRemove({}); - - if (ReturnCode::GENERAL_SUCCESS != status) - { - return status; - } - - return CommandSplitTunnelNewInstall(args); -} - ReturnCode CommandWintunDeleteDriver(const std::vector<std::wstring> &args) { ArgumentContext argsContext(args); @@ -336,9 +242,6 @@ int wmain(int argc, const wchar_t *argv[]) std::vector<CommandHandler> handlers = { - { L"st-evaluate", CommandSplitTunnelEvaluate }, - { L"st-new-install", CommandSplitTunnelNewInstall }, - { L"st-force-install", CommandSplitTunnelForceInstall }, { L"st-remove", CommandSplitTunnelRemove }, { L"wintun-delete-driver", CommandWintunDeleteDriver }, { L"wintun-delete-abandoned-device", CommandWintunDeleteAbandonedDevice }, diff --git a/windows/driverlogic/src/service.cpp b/windows/driverlogic/src/service.cpp index 14ed7880fa..cdaaf77ea9 100644 --- a/windows/driverlogic/src/service.cpp +++ b/windows/driverlogic/src/service.cpp @@ -43,29 +43,46 @@ private: TTime m_maxWaitTime; }; +SERVICE_STATUS_PROCESS GetServiceProcessStatus(SC_HANDLE service) +{ + SERVICE_STATUS_PROCESS ssp; + + DWORD bytesNeeded; + + auto status = QueryServiceStatusEx + ( + service, + SC_STATUS_PROCESS_INFO, + reinterpret_cast<BYTE*>(&ssp), + sizeof(ssp), + &bytesNeeded + ); + + if (status != 0) + { + return ssp; + } + + THROW_WINDOWS_ERROR(GetLastError(), "QueryServiceStatusEx"); +} + void WaitUntilServiceStopped(SC_HANDLE service, DWORD maxWaitMs) { TimeBox timer(maxWaitMs); for (;;) { - SERVICE_STATUS_PROCESS ssp; - - DWORD bytesNeeded; - - auto status = QueryServiceStatusEx - ( - service, - SC_STATUS_PROCESS_INFO, - reinterpret_cast<BYTE*>(&ssp), - sizeof(ssp), - &bytesNeeded - ); + try + { + const auto status = GetServiceProcessStatus(service); - if (status != 0 - && ssp.dwCurrentState == SERVICE_STOPPED) + if (status.dwCurrentState == SERVICE_STOPPED) + { + return; + } + } + catch (...) { - return; } if (timer.expired()) @@ -77,6 +94,44 @@ void WaitUntilServiceStopped(SC_HANDLE service, DWORD maxWaitMs) } } +bool ServiceIsRunning(const std::wstring &serviceName) +{ + const auto serviceManager = OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); + + if (serviceManager == NULL) + { + THROW_WINDOWS_ERROR(GetLastError(), "OpenSCManagerW"); + } + + common::memory::ScopeDestructor dtor; + + dtor += [serviceManager]() + { + CloseServiceHandle(serviceManager); + }; + + const auto service = OpenServiceW(serviceManager, serviceName.c_str(), SERVICE_ALL_ACCESS); + + if (service == NULL) + { + const auto error = GetLastError(); + + if (error != ERROR_SERVICE_DOES_NOT_EXIST) + { + THROW_WINDOWS_ERROR(error, "OpenServiceW"); + } + + return false; + } + + dtor += [service]() + { + CloseServiceHandle(service); + }; + + return GetServiceProcessStatus(service).dwCurrentState == SERVICE_RUNNING; +} + void PokeService(const std::wstring &serviceName, bool stopService, bool deleteService) { auto serviceManager = OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); @@ -97,7 +152,16 @@ void PokeService(const std::wstring &serviceName, bool stopService, bool deleteS if (service == NULL) { - THROW_WINDOWS_ERROR(GetLastError(), "OpenServiceW"); + const auto error = GetLastError(); + + if (error != ERROR_SERVICE_DOES_NOT_EXIST) + { + THROW_WINDOWS_ERROR(error, "OpenServiceW"); + } + + // If the service does not exist, we're done. + + return; } dtor += [service]() diff --git a/windows/driverlogic/src/service.h b/windows/driverlogic/src/service.h index 87632faf1b..423d16c0f0 100644 --- a/windows/driverlogic/src/service.h +++ b/windows/driverlogic/src/service.h @@ -2,6 +2,6 @@ #include <windows.h> -void WaitUntilServiceStopped(SC_HANDLE service, DWORD maxWaitMs); +bool ServiceIsRunning(const std::wstring &serviceName); void PokeService(const std::wstring &serviceName, bool stopService, bool deleteService); diff --git a/windows/driverlogic/src/version.cpp b/windows/driverlogic/src/version.cpp deleted file mode 100644 index d7394e224b..0000000000 --- a/windows/driverlogic/src/version.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "stdafx.h" -#include "version.h" -#include "device.h" -#include <setupapi.h> -#include <initguid.h> -#include <devpkey.h> -#include <libcommon/string.h> -#include <libcommon/memory.h> -#include <stdexcept> - -DRIVER_UPGRADE_STATUS -EvaluateDriverUpgrade -( - const std::wstring &existingVersion, - const std::wstring &proposedVersion -) -{ - // - // "x.y.z.a" - // - - using namespace common::string; - - auto et = Tokenize(existingVersion, L"."); - auto pt = Tokenize(proposedVersion, L"."); - - auto items = min(et.size(), pt.size()); - - for (auto index = 0; index < items; ++index) - { - auto ev = wcstoul(et[index].c_str(), nullptr, 10); - auto pv = wcstoul(pt[index].c_str(), nullptr, 10); - - if (pv > ev) - { - return DRIVER_UPGRADE_STATUS::WOULD_UPGRADE; - } - - if (ev > pv) - { - return DRIVER_UPGRADE_STATUS::WOULD_DOWNGRADE; - } - } - - if (pt.size() > et.size()) - { - return DRIVER_UPGRADE_STATUS::WOULD_UPGRADE; - } - - if (et.size() > pt.size()) - { - return DRIVER_UPGRADE_STATUS::WOULD_DOWNGRADE; - } - - return DRIVER_UPGRADE_STATUS::WOULD_INSTALL_SAME_VERSION; -} - -std::wstring -InfGetDriverVersion -( - const std::wstring &filePath -) -{ - auto infHandle = SetupOpenInfFileW(filePath.c_str(), nullptr, INF_STYLE_WIN4, nullptr); - - if (infHandle == INVALID_HANDLE_VALUE) - { - throw std::runtime_error("SetupOpenInfFileW()"); - } - - common::memory::ScopeDestructor dtor; - - dtor += [infHandle]() - { - SetupCloseInfFile(infHandle); - }; - - INFCONTEXT infContext { 0 }; - - auto status = SetupFindFirstLineW(infHandle, L"Version", L"DriverVer", &infContext); - - if (status == FALSE) - { - throw std::runtime_error("SetupFindFirstLineW()"); - } - - DWORD requiredSize; - - // - // This is a multi-value key. - // 0 = key, 1 = driver date - // - const DWORD VersionFieldIndex = 2; - - status = SetupGetStringFieldW(&infContext, VersionFieldIndex, nullptr, 0, &requiredSize); - - if (status == FALSE || requiredSize < 2) - { - throw std::runtime_error("SetupGetStringFieldW()"); - } - - std::vector<wchar_t> buffer(requiredSize); - - status = SetupGetStringFieldW(&infContext, VersionFieldIndex, - &buffer[0], static_cast<DWORD>(buffer.size()), nullptr); - - if (status == FALSE) - { - throw std::runtime_error("SetupGetStringFieldW()"); - } - - // Remove null terminator. - buffer.resize(requiredSize - 1); - - return buffer.data(); -} - -std::wstring -GetDriverVersion -( - const EnumeratedDevice &device -) -{ - return GetDeviceStringProperty(device.deviceInfoSet, device.deviceInfo, &DEVPKEY_Device_DriverVersion); -} diff --git a/windows/driverlogic/src/version.h b/windows/driverlogic/src/version.h deleted file mode 100644 index fc88b3b232..0000000000 --- a/windows/driverlogic/src/version.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include <string> -#include "device.h" - -enum class DRIVER_UPGRADE_STATUS -{ - WOULD_DOWNGRADE, - WOULD_INSTALL_SAME_VERSION, - WOULD_UPGRADE -}; - -DRIVER_UPGRADE_STATUS -EvaluateDriverUpgrade -( - const std::wstring &existingVersion, - const std::wstring &proposedVersion -); - -std::wstring -InfGetDriverVersion -( - const std::wstring &filePath -); - -std::wstring -GetDriverVersion -( - const EnumeratedDevice &device -); |
