Skip to content

Commit 4dc91b5

Browse files
committed
fix: create tunnel from scratch bug
closes #524
1 parent 7a3627b commit 4dc91b5

File tree

5 files changed

+107
-53
lines changed

5 files changed

+107
-53
lines changed

app/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ val versionCodeIncrement = with(getBuildTaskName().lowercase()) {
1414
when {
1515
this.contains(Constants.NIGHTLY) || this.contains(Constants.PRERELEASE) -> {
1616
if (versionFile.exists()) {
17-
versionFile.readText().toInt() + 1
17+
versionFile.readText().trim().toInt() + 1
1818
} else {
1919
1
2020
}

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt

+86-34
Original file line numberDiff line numberDiff line change
@@ -202,18 +202,14 @@ constructor(
202202

203203
private suspend fun handleVpnKillSwitchChange(enabled: Boolean) {
204204
withContext(ioDispatcher) {
205-
if (enabled) {
206-
Timber.d("Starting kill switch")
207-
val allowedIps = if (appDataRepository.settings.getSettings().isLanOnKillSwitchEnabled) {
208-
TunnelConfig.IPV4_PUBLIC_NETWORKS
209-
} else {
210-
emptySet()
211-
}
212-
tunnelService.get().setBackendState(BackendState.KILL_SWITCH_ACTIVE, allowedIps)
205+
if (!enabled) return@withContext tunnelService.get().setBackendState(BackendState.SERVICE_ACTIVE, emptySet())
206+
Timber.d("Starting kill switch")
207+
val allowedIps = if (appDataRepository.settings.getSettings().isLanOnKillSwitchEnabled) {
208+
TunnelConfig.IPV4_PUBLIC_NETWORKS
213209
} else {
214-
Timber.d("Sending shutdown of kill switch")
215-
tunnelService.get().setBackendState(BackendState.SERVICE_ACTIVE, emptySet())
210+
emptySet()
216211
}
212+
tunnelService.get().setBackendState(BackendState.KILL_SWITCH_ACTIVE, allowedIps)
217213
}
218214
}
219215

@@ -297,34 +293,76 @@ constructor(
297293
}
298294
}
299295

300-
fun saveConfigChanges(config: TunnelConfig, peers: List<PeerProxy>? = null, `interface`: InterfaceProxy? = null) = viewModelScope.launch(
301-
ioDispatcher,
302-
) {
296+
fun updateExistingTunnelConfig(
297+
tunnelConfig: TunnelConfig,
298+
tunnelName: String? = null,
299+
peers: List<PeerProxy>? = null,
300+
`interface`: InterfaceProxy? = null,
301+
) = viewModelScope.launch {
303302
runCatching {
304-
val amConfig = config.toAmConfig()
305-
val wgConfig = config.toWgConfig()
306-
rebuildConfigsAndSave(config, amConfig, wgConfig, peers, `interface`)
303+
val amConfig = tunnelConfig.toAmConfig()
304+
val wgConfig = tunnelConfig.toWgConfig()
305+
updateTunnelConfig(tunnelConfig, tunnelName, amConfig, wgConfig, peers, `interface`)
307306
_popBackStack.emit(true)
308307
SnackbarController.showMessage(StringValue.StringResource(R.string.config_changes_saved))
309308
}.onFailure {
310-
Timber.e(it)
311-
SnackbarController.showMessage(
312-
it.message?.let { message ->
313-
(StringValue.DynamicString(message))
314-
} ?: StringValue.StringResource(R.string.unknown_error),
309+
onConfigSaveError(it)
310+
}
311+
}
312+
313+
fun saveNewTunnel(tunnelName: String, peers: List<PeerProxy>, `interface`: InterfaceProxy) = viewModelScope.launch {
314+
runCatching {
315+
val config = buildConfigs(peers, `interface`)
316+
appDataRepository.tunnels.save(
317+
TunnelConfig(
318+
name = tunnelName,
319+
wgQuick = config.first.toWgQuickString(true),
320+
amQuick = config.second.toAwgQuickString(true),
321+
),
315322
)
323+
_popBackStack.emit(true)
324+
SnackbarController.showMessage(StringValue.StringResource(R.string.config_changes_saved))
325+
}.onFailure {
326+
onConfigSaveError(it)
316327
}
317328
}
318329

330+
private fun onConfigSaveError(throwable: Throwable) {
331+
Timber.e(throwable)
332+
SnackbarController.showMessage(
333+
throwable.message?.let { message ->
334+
(StringValue.DynamicString(message))
335+
} ?: StringValue.StringResource(R.string.unknown_error),
336+
)
337+
}
338+
339+
private suspend fun updateTunnelConfig(
340+
tunnelConfig: TunnelConfig,
341+
tunnelName: String? = null,
342+
amConfig: org.amnezia.awg.config.Config,
343+
wgConfig: Config,
344+
peers: List<PeerProxy>? = null,
345+
`interface`: InterfaceProxy? = null,
346+
) {
347+
val configs = rebuildConfigs(amConfig, wgConfig, peers, `interface`)
348+
appDataRepository.tunnels.save(
349+
tunnelConfig.copy(
350+
name = tunnelName ?: tunnelConfig.name,
351+
amQuick = configs.second.toAwgQuickString(true),
352+
wgQuick = configs.first.toWgQuickString(true),
353+
),
354+
)
355+
}
356+
319357
fun cleanUpUninstalledApps(tunnelConfig: TunnelConfig, packages: List<String>) = viewModelScope.launch(ioDispatcher) {
320358
runCatching {
321359
val amConfig = tunnelConfig.toAmConfig()
322360
val wgConfig = tunnelConfig.toWgConfig()
323361
val proxy = InterfaceProxy.from(amConfig.`interface`)
324362
if (proxy.includedApplications.isEmpty() && proxy.excludedApplications.isEmpty()) return@launch
325363
if (proxy.includedApplications.retainAll(packages.toSet()) || proxy.excludedApplications.retainAll(packages.toSet())) {
326-
Timber.i("Removing split tunnel package for app that no longer exists on the device")
327-
rebuildConfigsAndSave(tunnelConfig, amConfig, wgConfig, `interface` = proxy)
364+
updateTunnelConfig(tunnelConfig, amConfig = amConfig, wgConfig = wgConfig, `interface` = proxy)
365+
Timber.i("Removed split tunnel package for app that no longer exists on the device")
328366
}
329367
}.onFailure {
330368
Timber.e(it)
@@ -340,24 +378,38 @@ constructor(
340378
)
341379
}
342380

343-
private suspend fun rebuildConfigsAndSave(
344-
config: TunnelConfig,
381+
private suspend fun rebuildConfigs(
345382
amConfig: org.amnezia.awg.config.Config,
346383
wgConfig: Config,
347384
peers: List<PeerProxy>? = null,
348385
`interface`: InterfaceProxy? = null,
349-
) {
350-
appDataRepository.tunnels.save(
351-
config.copy(
352-
wgQuick = Config.Builder().apply {
386+
): Pair<Config, org.amnezia.awg.config.Config> {
387+
return withContext(ioDispatcher) {
388+
Pair(
389+
Config.Builder().apply {
353390
addPeers(peers?.map { it.toWgPeer() } ?: wgConfig.peers)
354391
setInterface(`interface`?.toWgInterface() ?: wgConfig.`interface`)
355-
}.build().toWgQuickString(true),
356-
amQuick = org.amnezia.awg.config.Config.Builder().apply {
392+
}.build(),
393+
org.amnezia.awg.config.Config.Builder().apply {
357394
addPeers(peers?.map { it.toAmPeer() } ?: amConfig.peers)
358395
setInterface(`interface`?.toAmInterface() ?: amConfig.`interface`)
359-
}.build().toAwgQuickString(true),
360-
),
361-
)
396+
}.build(),
397+
)
398+
}
399+
}
400+
401+
private suspend fun buildConfigs(peers: List<PeerProxy>, `interface`: InterfaceProxy): Pair<Config, org.amnezia.awg.config.Config> {
402+
return withContext(ioDispatcher) {
403+
Pair(
404+
Config.Builder().apply {
405+
addPeers(peers.map { it.toWgPeer() })
406+
setInterface(`interface`.toWgInterface())
407+
}.build(),
408+
org.amnezia.awg.config.Config.Builder().apply {
409+
addPeers(peers.map { it.toAmPeer() })
410+
setInterface(`interface`.toAmInterface())
411+
}.build(),
412+
)
413+
}
362414
}
363415
}

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/TunnelOptionsScreen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ fun OptionsScreen(appViewModel: AppViewModel, appUiState: AppUiState, tunnelId:
155155
val amneziaClick = {
156156
val proxy = InterfaceProxy.from(amConfig.`interface`)
157157
val `interface` = if (!isAmneziaCompatibilityEnabled) proxy.toAmneziaCompatibilityConfig() else proxy.resetAmneziaProperties()
158-
appViewModel.saveConfigChanges(config, `interface` = `interface`)
158+
appViewModel.updateExistingTunnelConfig(config, `interface` = `interface`)
159159
}
160160
GroupLabel(stringResource(R.string.quick_actions))
161161
SurfaceSelectionGroupButton(

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/config/ConfigScreen.kt

+18-16
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
8383

8484
val tunnelConfig by remember {
8585
derivedStateOf {
86-
appUiState.tunnels.first { it.id == tunnelId }
86+
appUiState.tunnels.firstOrNull { it.id == tunnelId }
8787
}
8888
}
8989

9090
val configPair by remember {
9191
derivedStateOf {
92-
Pair(tunnelConfig.name, tunnelConfig.toAmConfig())
92+
Pair(tunnelConfig?.name ?: "", tunnelConfig?.toAmConfig())
9393
}
9494
}
9595

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

100100
var interfaceState by remember {
101-
mutableStateOf(InterfaceProxy.from(configPair.second.`interface`))
101+
mutableStateOf(configPair.second?.let { InterfaceProxy.from(it.`interface`) } ?: InterfaceProxy())
102102
}
103103

104104
var showAmneziaValues by remember {
105-
mutableStateOf(configPair.second.`interface`.junkPacketCount.isPresent)
105+
mutableStateOf(configPair.second?.`interface`?.junkPacketCount?.isPresent == true)
106106
}
107107

108108
var showScripts by remember {
109109
mutableStateOf(false)
110110
}
111111

112112
val peersState = remember {
113-
configPair.second.peers.map { PeerProxy.from(it) }.toMutableStateList()
113+
(configPair.second?.peers?.map { PeerProxy.from(it) } ?: listOf(PeerProxy())).toMutableStateList()
114114
}
115115

116116
var showAuthPrompt by remember { mutableStateOf(false) }
@@ -148,13 +148,14 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
148148
topBar = {
149149
TopNavBar(stringResource(R.string.edit_tunnel), trailing = {
150150
IconButton(onClick = {
151-
appViewModel.saveConfigChanges(
152-
tunnelConfig.copy(
153-
name = tunnelName,
154-
),
155-
peers = peersState,
156-
`interface` = interfaceState,
157-
)
151+
tunnelConfig?.let {
152+
appViewModel.updateExistingTunnelConfig(
153+
it,
154+
tunnelName,
155+
peersState,
156+
interfaceState,
157+
)
158+
} ?: appViewModel.saveNewTunnel(tunnelName, peersState, interfaceState)
158159
}) {
159160
val icon = Icons.Outlined.Save
160161
Icon(
@@ -226,6 +227,7 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
226227
Modifier
227228
.fillMaxWidth(),
228229
)
230+
val privateKeyEnabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated
229231
OutlinedTextField(
230232
textStyle = MaterialTheme.typography.labelLarge,
231233
modifier =
@@ -234,16 +236,16 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
234236
.clickable { showAuthPrompt = true },
235237
value = interfaceState.privateKey,
236238
visualTransformation =
237-
if ((tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated) {
239+
if (privateKeyEnabled) {
238240
VisualTransformation.None
239241
} else {
240242
PasswordVisualTransformation()
241243
},
242-
enabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated,
244+
enabled = privateKeyEnabled,
243245
onValueChange = { interfaceState = interfaceState.copy(privateKey = it) },
244246
trailingIcon = {
245247
IconButton(
246-
enabled = isAuthenticated,
248+
enabled = privateKeyEnabled,
247249
modifier = Modifier.focusRequester(FocusRequester.Default),
248250
onClick = {
249251
val keypair = KeyPair()
@@ -256,7 +258,7 @@ fun ConfigScreen(appUiState: AppUiState, appViewModel: AppViewModel, tunnelId: I
256258
Icon(
257259
Icons.Rounded.Refresh,
258260
stringResource(R.string.rotate_keys),
259-
tint = if (isAuthenticated) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline,
261+
tint = if (privateKeyEnabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline,
260262
)
261263
}
262264
},

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunneloptions/splittunnel/SplitTunnelScreen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ fun SplitTunnelScreen(appUiState: AppUiState, tunnelId: Int, viewModel: AppViewM
127127
}
128128
SplitOptions.ALL -> Unit
129129
}
130-
viewModel.saveConfigChanges(config, `interface` = proxyInterface)
130+
viewModel.updateExistingTunnelConfig(config, `interface` = proxyInterface)
131131
}) {
132132
val icon = Icons.Outlined.Save
133133
Icon(

0 commit comments

Comments
 (0)