Skip to content

Commit

Permalink
fix: create tunnel from scratch bug
Browse files Browse the repository at this point in the history
closes #524
zaneschepke committed Dec 29, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 7a3627b commit 4dc91b5
Showing 5 changed files with 107 additions and 53 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ val versionCodeIncrement = with(getBuildTaskName().lowercase()) {
when {
this.contains(Constants.NIGHTLY) || this.contains(Constants.PRERELEASE) -> {
if (versionFile.exists()) {
versionFile.readText().toInt() + 1
versionFile.readText().trim().toInt() + 1
} else {
1
}
Original file line number Diff line number Diff line change
@@ -202,18 +202,14 @@ constructor(

private suspend fun handleVpnKillSwitchChange(enabled: Boolean) {
withContext(ioDispatcher) {
if (enabled) {
Timber.d("Starting kill switch")
val allowedIps = if (appDataRepository.settings.getSettings().isLanOnKillSwitchEnabled) {
TunnelConfig.IPV4_PUBLIC_NETWORKS
} else {
emptySet()
}
tunnelService.get().setBackendState(BackendState.KILL_SWITCH_ACTIVE, allowedIps)
if (!enabled) return@withContext tunnelService.get().setBackendState(BackendState.SERVICE_ACTIVE, emptySet())
Timber.d("Starting kill switch")
val allowedIps = if (appDataRepository.settings.getSettings().isLanOnKillSwitchEnabled) {
TunnelConfig.IPV4_PUBLIC_NETWORKS
} else {
Timber.d("Sending shutdown of kill switch")
tunnelService.get().setBackendState(BackendState.SERVICE_ACTIVE, emptySet())
emptySet()
}
tunnelService.get().setBackendState(BackendState.KILL_SWITCH_ACTIVE, allowedIps)
}
}

@@ -297,34 +293,76 @@ constructor(
}
}

fun saveConfigChanges(config: TunnelConfig, peers: List<PeerProxy>? = null, `interface`: InterfaceProxy? = null) = viewModelScope.launch(
ioDispatcher,
) {
fun updateExistingTunnelConfig(
tunnelConfig: TunnelConfig,
tunnelName: String? = null,
peers: List<PeerProxy>? = null,
`interface`: InterfaceProxy? = null,
) = viewModelScope.launch {
runCatching {
val amConfig = config.toAmConfig()
val wgConfig = config.toWgConfig()
rebuildConfigsAndSave(config, amConfig, wgConfig, peers, `interface`)
val amConfig = tunnelConfig.toAmConfig()
val wgConfig = tunnelConfig.toWgConfig()
updateTunnelConfig(tunnelConfig, tunnelName, amConfig, wgConfig, peers, `interface`)
_popBackStack.emit(true)
SnackbarController.showMessage(StringValue.StringResource(R.string.config_changes_saved))
}.onFailure {
Timber.e(it)
SnackbarController.showMessage(
it.message?.let { message ->
(StringValue.DynamicString(message))
} ?: StringValue.StringResource(R.string.unknown_error),
onConfigSaveError(it)
}
}

fun saveNewTunnel(tunnelName: String, peers: List<PeerProxy>, `interface`: InterfaceProxy) = viewModelScope.launch {
runCatching {
val config = buildConfigs(peers, `interface`)
appDataRepository.tunnels.save(
TunnelConfig(
name = tunnelName,
wgQuick = config.first.toWgQuickString(true),
amQuick = config.second.toAwgQuickString(true),
),
)
_popBackStack.emit(true)
SnackbarController.showMessage(StringValue.StringResource(R.string.config_changes_saved))
}.onFailure {
onConfigSaveError(it)
}
}

private fun onConfigSaveError(throwable: Throwable) {
Timber.e(throwable)
SnackbarController.showMessage(
throwable.message?.let { message ->
(StringValue.DynamicString(message))
} ?: StringValue.StringResource(R.string.unknown_error),
)
}

private suspend fun updateTunnelConfig(
tunnelConfig: TunnelConfig,
tunnelName: String? = null,
amConfig: org.amnezia.awg.config.Config,
wgConfig: Config,
peers: List<PeerProxy>? = null,
`interface`: InterfaceProxy? = null,
) {
val configs = rebuildConfigs(amConfig, wgConfig, peers, `interface`)
appDataRepository.tunnels.save(
tunnelConfig.copy(
name = tunnelName ?: tunnelConfig.name,
amQuick = configs.second.toAwgQuickString(true),
wgQuick = configs.first.toWgQuickString(true),
),
)
}

fun cleanUpUninstalledApps(tunnelConfig: TunnelConfig, packages: List<String>) = viewModelScope.launch(ioDispatcher) {
runCatching {
val amConfig = tunnelConfig.toAmConfig()
val wgConfig = tunnelConfig.toWgConfig()
val proxy = InterfaceProxy.from(amConfig.`interface`)
if (proxy.includedApplications.isEmpty() && proxy.excludedApplications.isEmpty()) return@launch
if (proxy.includedApplications.retainAll(packages.toSet()) || proxy.excludedApplications.retainAll(packages.toSet())) {
Timber.i("Removing split tunnel package for app that no longer exists on the device")
rebuildConfigsAndSave(tunnelConfig, amConfig, wgConfig, `interface` = proxy)
updateTunnelConfig(tunnelConfig, amConfig = amConfig, wgConfig = wgConfig, `interface` = proxy)
Timber.i("Removed split tunnel package for app that no longer exists on the device")
}
}.onFailure {
Timber.e(it)
@@ -340,24 +378,38 @@ constructor(
)
}

private suspend fun rebuildConfigsAndSave(
config: TunnelConfig,
private suspend fun rebuildConfigs(
amConfig: org.amnezia.awg.config.Config,
wgConfig: Config,
peers: List<PeerProxy>? = null,
`interface`: InterfaceProxy? = null,
) {
appDataRepository.tunnels.save(
config.copy(
wgQuick = Config.Builder().apply {
): Pair<Config, org.amnezia.awg.config.Config> {
return withContext(ioDispatcher) {
Pair(
Config.Builder().apply {
addPeers(peers?.map { it.toWgPeer() } ?: wgConfig.peers)
setInterface(`interface`?.toWgInterface() ?: wgConfig.`interface`)
}.build().toWgQuickString(true),
amQuick = org.amnezia.awg.config.Config.Builder().apply {
}.build(),
org.amnezia.awg.config.Config.Builder().apply {
addPeers(peers?.map { it.toAmPeer() } ?: amConfig.peers)
setInterface(`interface`?.toAmInterface() ?: amConfig.`interface`)
}.build().toAwgQuickString(true),
),
)
}.build(),
)
}
}

private suspend fun buildConfigs(peers: List<PeerProxy>, `interface`: InterfaceProxy): Pair<Config, org.amnezia.awg.config.Config> {
return withContext(ioDispatcher) {
Pair(
Config.Builder().apply {
addPeers(peers.map { it.toWgPeer() })
setInterface(`interface`.toWgInterface())
}.build(),
org.amnezia.awg.config.Config.Builder().apply {
addPeers(peers.map { it.toAmPeer() })
setInterface(`interface`.toAmInterface())
}.build(),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ fun OptionsScreen(appViewModel: AppViewModel, appUiState: AppUiState, tunnelId:
val amneziaClick = {
val proxy = InterfaceProxy.from(amConfig.`interface`)
val `interface` = if (!isAmneziaCompatibilityEnabled) proxy.toAmneziaCompatibilityConfig() else proxy.resetAmneziaProperties()
appViewModel.saveConfigChanges(config, `interface` = `interface`)
appViewModel.updateExistingTunnelConfig(config, `interface` = `interface`)
}
GroupLabel(stringResource(R.string.quick_actions))
SurfaceSelectionGroupButton(
Original file line number Diff line number Diff line change
@@ -83,13 +83,13 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I

val tunnelConfig by remember {
derivedStateOf {
appUiState.tunnels.first { it.id == tunnelId }
appUiState.tunnels.firstOrNull { it.id == tunnelId }
}
}

val configPair by remember {
derivedStateOf {
Pair(tunnelConfig.name, tunnelConfig.toAmConfig())
Pair(tunnelConfig?.name ?: "", tunnelConfig?.toAmConfig())
}
}

@@ -98,19 +98,19 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
}

var interfaceState by remember {
mutableStateOf(InterfaceProxy.from(configPair.second.`interface`))
mutableStateOf(configPair.second?.let { InterfaceProxy.from(it.`interface`) } ?: InterfaceProxy())
}

var showAmneziaValues by remember {
mutableStateOf(configPair.second.`interface`.junkPacketCount.isPresent)
mutableStateOf(configPair.second?.`interface`?.junkPacketCount?.isPresent == true)
}

var showScripts by remember {
mutableStateOf(false)
}

val peersState = remember {
configPair.second.peers.map { PeerProxy.from(it) }.toMutableStateList()
(configPair.second?.peers?.map { PeerProxy.from(it) } ?: listOf(PeerProxy())).toMutableStateList()
}

var showAuthPrompt by remember { mutableStateOf(false) }
@@ -148,13 +148,14 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
topBar = {
TopNavBar(stringResource(R.string.edit_tunnel), trailing = {
IconButton(onClick = {
appViewModel.saveConfigChanges(
tunnelConfig.copy(
name = tunnelName,
),
peers = peersState,
`interface` = interfaceState,
)
tunnelConfig?.let {
appViewModel.updateExistingTunnelConfig(
it,
tunnelName,
peersState,
interfaceState,
)
} ?: appViewModel.saveNewTunnel(tunnelName, peersState, interfaceState)
}) {
val icon = Icons.Outlined.Save
Icon(
@@ -226,6 +227,7 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
Modifier
.fillMaxWidth(),
)
val privateKeyEnabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated
OutlinedTextField(
textStyle = MaterialTheme.typography.labelLarge,
modifier =
@@ -234,16 +236,16 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
.clickable { showAuthPrompt = true },
value = interfaceState.privateKey,
visualTransformation =
if ((tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated) {
if (privateKeyEnabled) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
},
enabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated,
enabled = privateKeyEnabled,
onValueChange = { interfaceState = interfaceState.copy(privateKey = it) },
trailingIcon = {
IconButton(
enabled = isAuthenticated,
enabled = privateKeyEnabled,
modifier = Modifier.focusRequester(FocusRequester.Default),
onClick = {
val keypair = KeyPair()
@@ -256,7 +258,7 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
Icon(
Icons.Rounded.Refresh,
stringResource(R.string.rotate_keys),
tint = if (isAuthenticated) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline,
tint = if (privateKeyEnabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline,
)
}
},
Original file line number Diff line number Diff line change
@@ -127,7 +127,7 @@ fun SplitTunnelScreen(appUiState: AppUiState, tunnelId: Int, viewModel: AppViewM
}
SplitOptions.ALL -> Unit
}
viewModel.saveConfigChanges(config, `interface` = proxyInterface)
viewModel.updateExistingTunnelConfig(config, `interface` = proxyInterface)
}) {
val icon = Icons.Outlined.Save
Icon(

0 comments on commit 4dc91b5

Please sign in to comment.