From 11e385d3501392c38f1b1eefc5c1e13516519973 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Fri, 27 Dec 2024 02:52:44 -0500 Subject: [PATCH] feat: toggle to set amnezia/wg compatibility values closes #469 closes #518 --- .../wireguardautotunnel/ui/AppViewModel.kt | 9 ++ .../wireguardautotunnel/ui/MainActivity.kt | 1 + .../tunneloptions/TunnelOptionsScreen.kt | 53 +++++++++-- .../tunneloptions/TunnelOptionsViewModel.kt | 88 ------------------- .../splittunnel/SplitTunnelScreen.kt | 2 +- .../TunnelAutoTunnelViewModel.kt | 9 -- .../util/extensions/TunnelExtensions.kt | 45 ++++++++++ app/src/main/res/values/strings.xml | 3 + 8 files changed, 107 insertions(+), 103 deletions(-) delete mode 100644 app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsViewModel.kt diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt index dd0307f..c2ea8a1 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt @@ -331,6 +331,15 @@ constructor( } } + fun onTogglePrimaryTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch { + appDataRepository.tunnels.updatePrimaryTunnel( + when (tunnelConfig.isPrimaryTunnel) { + true -> null + false -> tunnelConfig + }, + ) + } + private suspend fun rebuildConfigsAndSave( config: TunnelConfig, amConfig: org.amnezia.awg.config.Config, diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt index 22f6c86..613ea86 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt @@ -227,6 +227,7 @@ class MainActivity : AppCompatActivity() { OptionsScreen( tunnelId = args.id, appUiState = appUiState, + appViewModel = viewModel, ) } composable { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsScreen.kt index 63684c9..0c42e6d 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.CallSplit +import androidx.compose.material.icons.outlined.Adjust import androidx.compose.material.icons.outlined.Bolt import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Star @@ -24,24 +25,35 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.ui.AppUiState +import com.zaneschepke.wireguardautotunnel.ui.AppViewModel import com.zaneschepke.wireguardautotunnel.ui.Route import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton +import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton +import com.zaneschepke.wireguardautotunnel.ui.screens.tunneloptions.config.model.InterfaceProxy +import com.zaneschepke.wireguardautotunnel.util.extensions.isWgCompatibilityMode +import com.zaneschepke.wireguardautotunnel.util.extensions.resetAmneziaProperties import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth +import com.zaneschepke.wireguardautotunnel.util.extensions.toAmneziaCompatibilityConfig @Composable -fun OptionsScreen(tunnelOptionsViewModel: TunnelOptionsViewModel = hiltViewModel(), appUiState: AppUiState, tunnelId: Int) { +fun OptionsScreen(appViewModel: AppViewModel, appUiState: AppUiState, tunnelId: Int) { val navController = LocalNavController.current val config = appUiState.tunnels.first { it.id == tunnelId } + // TODO optimize + + val amConfig = config.toAmConfig() + + val isAmneziaCompatibilityEnabled = amConfig.`interface`.isWgCompatibilityMode() + var currentText by remember { mutableStateOf("") } LaunchedEffect(config.tunnelNetworks) { @@ -82,10 +94,10 @@ fun OptionsScreen(tunnelOptionsViewModel: TunnelOptionsViewModel = hiltViewModel trailing = { ScaledSwitch( config.isPrimaryTunnel, - onClick = { tunnelOptionsViewModel.onTogglePrimaryTunnel(config) }, + onClick = { appViewModel.onTogglePrimaryTunnel(config) }, ) }, - onClick = { tunnelOptionsViewModel.onTogglePrimaryTunnel(config) }, + onClick = { appViewModel.onTogglePrimaryTunnel(config) }, ), SelectionItem( Icons.Outlined.Bolt, @@ -140,7 +152,38 @@ fun OptionsScreen(tunnelOptionsViewModel: TunnelOptionsViewModel = hiltViewModel ), ), ) -// GroupLabel(stringResource(R.string.quick_actions)) + val amneziaClick = { + val proxy = InterfaceProxy.from(amConfig.`interface`) + val `interface` = if (!isAmneziaCompatibilityEnabled) proxy.toAmneziaCompatibilityConfig() else proxy.resetAmneziaProperties() + appViewModel.saveConfigChanges(config, `interface` = `interface`) + } + GroupLabel(stringResource(R.string.quick_actions)) + SurfaceSelectionGroupButton( + listOf( + SelectionItem( + Icons.Outlined.Adjust, + title = { + Text( + stringResource(R.string.enable_amnezia), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + ) + }, + description = { + Text( + stringResource(R.string.wg_compat_mode), + style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline), + ) + }, + trailing = { + ScaledSwitch( + isAmneziaCompatibilityEnabled, + onClick = { amneziaClick() }, + ) + }, + onClick = { amneziaClick() }, + ), + ), + ) } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsViewModel.kt deleted file mode 100644 index 6fecade..0000000 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsViewModel.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.zaneschepke.wireguardautotunnel.ui.screens.tunneloptions - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.zaneschepke.wireguardautotunnel.R -import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig -import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository -import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController -import com.zaneschepke.wireguardautotunnel.util.StringValue -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class TunnelOptionsViewModel -@Inject -constructor( - private val appDataRepository: AppDataRepository, -) : ViewModel() { - - fun onDeleteRunSSID(ssid: String, tunnelConfig: TunnelConfig) = viewModelScope.launch { - appDataRepository.tunnels.save( - tunnelConfig = - tunnelConfig.copy( - tunnelNetworks = (tunnelConfig.tunnelNetworks - ssid).toMutableList(), - ), - ) - } - - fun saveTunnelChanges(tunnelConfig: TunnelConfig) = viewModelScope.launch { - appDataRepository.tunnels.save(tunnelConfig) - } - - fun onSaveRunSSID(ssid: String, tunnelConfig: TunnelConfig) = viewModelScope.launch { - if (ssid.isBlank()) return@launch - val trimmed = ssid.trim() - val tunnelsWithName = appDataRepository.tunnels.findByTunnelNetworksName(trimmed) - - if (!tunnelConfig.tunnelNetworks.contains(trimmed) && - tunnelsWithName.isEmpty() - ) { - saveTunnelChanges( - tunnelConfig.copy( - tunnelNetworks = (tunnelConfig.tunnelNetworks + ssid).toMutableList(), - ), - ) - } else { - SnackbarController.showMessage( - StringValue.StringResource( - R.string.error_ssid_exists, - ), - ) - } - } - - fun onToggleIsMobileDataTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch { - if (tunnelConfig.isMobileDataTunnel) { - appDataRepository.tunnels.updateMobileDataTunnel(null) - } else { - appDataRepository.tunnels.updateMobileDataTunnel(tunnelConfig) - } - } - - fun onTogglePrimaryTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch { - appDataRepository.tunnels.updatePrimaryTunnel( - when (tunnelConfig.isPrimaryTunnel) { - true -> null - false -> tunnelConfig - }, - ) - } - - fun onToggleRestartOnPing(tunnelConfig: TunnelConfig) = viewModelScope.launch { - appDataRepository.tunnels.save( - tunnelConfig.copy( - isPingEnabled = !tunnelConfig.isPingEnabled, - ), - ) - } - - fun onToggleIsEthernetTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch { - if (tunnelConfig.isEthernetTunnel) { - appDataRepository.tunnels.updateEthernetTunnel(null) - } else { - appDataRepository.tunnels.updateEthernetTunnel(tunnelConfig) - } - } -} diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/splittunnel/SplitTunnelScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/splittunnel/SplitTunnelScreen.kt index d7225dd..150f5c3 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/splittunnel/SplitTunnelScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/splittunnel/SplitTunnelScreen.kt @@ -87,7 +87,7 @@ fun SplitTunnelScreen(appUiState: AppUiState, tunnelId: Int, viewModel: AppViewM val selectedPackages = remember { mutableStateListOf() } LaunchedEffect(Unit) { - proxyInterface = InterfaceProxy.from(config.toWgConfig().`interface`) + proxyInterface = InterfaceProxy.from(config.toAmConfig().`interface`) val pair = when { proxyInterface.excludedApplications.isNotEmpty() -> Pair(SplitOptions.EXCLUDE, proxyInterface.excludedApplications) proxyInterface.includedApplications.isNotEmpty() -> Pair(SplitOptions.INCLUDE, proxyInterface.includedApplications) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/tunnelautotunnel/TunnelAutoTunnelViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/tunnelautotunnel/TunnelAutoTunnelViewModel.kt index 3cbe737..0a9de2f 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/tunnelautotunnel/TunnelAutoTunnelViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/tunnelautotunnel/TunnelAutoTunnelViewModel.kt @@ -61,15 +61,6 @@ constructor( } } - fun onTogglePrimaryTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch { - appDataRepository.tunnels.updatePrimaryTunnel( - when (tunnelConfig.isPrimaryTunnel) { - true -> null - false -> tunnelConfig - }, - ) - } - fun onToggleRestartOnPing(tunnelConfig: TunnelConfig) = viewModelScope.launch { appDataRepository.tunnels.save( tunnelConfig.copy( diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt index e49ef3a..7e813ab 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt @@ -6,14 +6,17 @@ import com.wireguard.config.Peer import com.zaneschepke.wireguardautotunnel.service.tunnel.BackendState import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics +import com.zaneschepke.wireguardautotunnel.ui.screens.tunneloptions.config.model.InterfaceProxy import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree import com.zaneschepke.wireguardautotunnel.ui.theme.Straw import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.NumberUtils import org.amnezia.awg.backend.Backend import org.amnezia.awg.config.Config +import org.amnezia.awg.config.Interface import timber.log.Timber import java.net.InetAddress +import kotlin.jvm.optionals.getOrNull fun TunnelStatistics.mapPeerStats(): Map { return this.getPeers().associateWith { key -> (this.peerStats(key)) } @@ -95,3 +98,45 @@ fun Backend.BackendState.asBackendState(): BackendState { fun BackendState.asAmBackendState(): Backend.BackendState { return Backend.BackendState.valueOf(this.name) } + +fun Interface.isWgCompatibilityMode(): Boolean { + return ( + junkPacketCount.getOrNull() in 3..<5 && + junkPacketMinSize.getOrNull() == 40 && + junkPacketMaxSize.getOrNull() == 70 && + with(initPacketJunkSize.getOrNull()) { this == 0 || this == null } && + with(responsePacketJunkSize.getOrNull()) { this == 0 || this == null } && + initPacketMagicHeader.getOrNull() == 1L && + responsePacketMagicHeader.getOrNull() == 2L && + underloadPacketMagicHeader.getOrNull() == 3L && + transportPacketMagicHeader.getOrNull() == 4L + ) +} + +fun InterfaceProxy.toAmneziaCompatibilityConfig(): InterfaceProxy { + return copy( + junkPacketCount = "4", + junkPacketMinSize = "40", + junkPacketMaxSize = "70", + initPacketJunkSize = "0", + responsePacketJunkSize = "0", + initPacketMagicHeader = "1", + responsePacketMagicHeader = "2", + underloadPacketMagicHeader = "3", + transportPacketMagicHeader = "4", + ) +} + +fun InterfaceProxy.resetAmneziaProperties(): InterfaceProxy { + return copy( + junkPacketCount = "", + junkPacketMinSize = "", + junkPacketMaxSize = "", + initPacketJunkSize = "", + responsePacketJunkSize = "", + initPacketMagicHeader = "", + responsePacketMagicHeader = "", + underloadPacketMagicHeader = "", + transportPacketMagicHeader = "", + ) +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4eb9c37..3e825e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -191,4 +191,7 @@ Pre down Post down Amnezia unavailable in kernel mode + Enable Amnezia + WG compatibility mode + Quick actions