Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace old waitForTunnelUp function #7458

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1e1b9d0
Replace old waitForTunnelUp function
Rawa Jan 13, 2025
ea5030e
Add network state to classes.rs
Rawa Jan 27, 2025
ba47c26
WIP Call `waitForRoutes` from Rust
MarkusPettersson98 Jan 23, 2025
fc2a1a6
Heavy wip
MarkusPettersson98 Jan 28, 2025
59296d6
Add jnix patch
hulthe Jan 28, 2025
7ec2c30
wip java types
hulthe Jan 28, 2025
e59aa41
TODO(drop): Use custom branch of jnix
MarkusPettersson98 Jan 28, 2025
44896ac
WIP Add some types
MarkusPettersson98 Jan 28, 2025
609ea8d
Fix routes
Rawa Jan 28, 2025
f7dc879
Add route info to classes.rs
Rawa Jan 28, 2025
83e8255
It compiles again!
MarkusPettersson98 Jan 28, 2025
e2d2925
Pass AndroidContext to Routemanager
MarkusPettersson98 Jan 28, 2025
bdd5396
Update method name
Rawa Jan 28, 2025
706e4c8
Fix do not crash if NetworkState is null
MarkusPettersson98 Jan 28, 2025
93c92c4
Do not clone androidcontext for routemanager
MarkusPettersson98 Jan 28, 2025
ea3d09c
Do not listen on notify change
MarkusPettersson98 Jan 28, 2025
1afd753
TODO(drop) Log startup
MarkusPettersson98 Jan 28, 2025
0f42381
Remove error that does not exist
MarkusPettersson98 Jan 28, 2025
179d018
Actually receive notifications about networkstate
MarkusPettersson98 Jan 28, 2025
b3bb654
Fix wrongly named struct member
MarkusPettersson98 Jan 28, 2025
00e7c49
Wait for routes update in WireguardMonitor::start
MarkusPettersson98 Jan 28, 2025
6cdc050
Do not immediately error out in tsm
MarkusPettersson98 Jan 28, 2025
b88cd5c
Dont get stuck in connecting
MarkusPettersson98 Jan 28, 2025
c6b3ab6
Replace `todo` with _an_ error
MarkusPettersson98 Jan 28, 2025
b105972
Fix remaining warnings
MarkusPettersson98 Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ license = "GPL-3.0"
edition = "2021"
rust-version = "1.80.0"

[patch.crates-io]
jnix = { git = "https://github.com/mullvad/jnix.git", branch = "i16-from-java" }

[workspace]
resolver = "2"
members = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ private fun ErrorState.message(): String {
val cause = this.cause
return when {
isBlocking -> cause.errorMessageId()
cause is ErrorStateCause.RoutesTimedOut -> "Yo routes is fukkkd"
else -> stringResource(R.string.failed_to_block_internet)
}
}
Expand All @@ -223,6 +224,8 @@ private fun ErrorStateCause.errorMessageId(): String =
R.string.invalid_dns_servers,
addresses.joinToString { address -> address.addressString() },
)

ErrorStateCause.RoutesTimedOut -> "Yo routes is fukkked"
}

private fun AuthFailedError.errorMessageId(): Int =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package net.mullvad.mullvadvpn.lib.common.util

import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.net.VpnService.prepare
import android.os.ParcelFileDescriptor
import arrow.core.Either
import arrow.core.flatten
import arrow.core.flatMap
import arrow.core.left
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import arrow.core.right
import co.touchlab.kermit.Logger
import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.getInstalledPackagesList
import net.mullvad.mullvadvpn.lib.model.PrepareError
import net.mullvad.mullvadvpn.lib.model.Prepared

/**
* Safely prepare to establish a VPN connection.
*
* Invoking VpnService.prepare() can result in 3 out comes:
* 1. IllegalStateException - There is a legacy VPN profile marked as always on
* 2. Intent
Expand All @@ -34,7 +40,7 @@ fun Context.prepareVpnSafe(): Either<PrepareError, Prepared> =
else -> throw it
}
}
.map { intent ->
.flatMap { intent ->
if (intent == null) {
Prepared.right()
} else {
Expand All @@ -46,7 +52,6 @@ fun Context.prepareVpnSafe(): Either<PrepareError, Prepared> =
}
}
}
.flatten()

fun Context.getAlwaysOnVpnAppName(): String? {
return resolveAlwaysOnVpnPackageName()
Expand All @@ -59,3 +64,38 @@ fun Context.getAlwaysOnVpnAppName(): String? {
?.loadLabel(packageManager)
?.toString()
}

/**
* Establish a VPN connection safely.
*
* This function wraps the [VpnService.Builder.establish] function and catches any exceptions that
* may be thrown and type them to a more specific error.
*
* @return [ParcelFileDescriptor] if successful, [EstablishError] otherwise
*/
fun VpnService.Builder.establishSafe(): Either<EstablishError, ParcelFileDescriptor> = either {
val vpnInterfaceFd =
Either.catch { establish() }
.mapLeft {
when (it) {
is IllegalStateException -> EstablishError.ParameterNotApplied(it)
is IllegalArgumentException -> EstablishError.ParameterNotAccepted(it)
else -> EstablishError.UnknownError(it)
}
}
.bind()

ensureNotNull(vpnInterfaceFd) { EstablishError.NullVpnInterface }

vpnInterfaceFd
}

sealed interface EstablishError {
data class ParameterNotApplied(val exception: IllegalStateException) : EstablishError

data class ParameterNotAccepted(val exception: IllegalArgumentException) : EstablishError

data object NullVpnInterface : EstablishError

data class UnknownError(val error: Throwable) : EstablishError
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ internal fun ManagementInterface.ErrorState.toDomain(
ManagementInterface.ErrorState.Cause.OTHER_LEGACY_ALWAYS_ON_VPN ->
ErrorStateCause.OtherLegacyAlwaysOnApp
ManagementInterface.ErrorState.Cause.INVALID_DNS_SERVERS -> invalidDnsServers!!
ManagementInterface.ErrorState.Cause.ROUTES_TIMED_OUT ->
ErrorStateCause.RoutesTimedOut
},
isBlocking = !hasBlockingError(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ sealed class ErrorStateCause {

data object StartTunnelError : ErrorStateCause()

data object RoutesTimedOut : ErrorStateCause()

data class TunnelParameterError(val error: ParameterGenerationError) : ErrorStateCause()

data object IsOffline : ErrorStateCause()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import android.net.NetworkCapabilities
import android.net.NetworkRequest
import co.touchlab.kermit.Logger
import java.net.InetAddress
import kotlin.collections.ArrayList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
import net.mullvad.talpid.model.NetworkState as DaemonNetworkState
import net.mullvad.talpid.util.NetworkEvent
import net.mullvad.talpid.util.defaultNetworkFlow
import net.mullvad.talpid.util.NetworkState
import net.mullvad.talpid.util.defaultNetworkStateFlow
import net.mullvad.talpid.util.networkFlow

class ConnectivityListener(val connectivityManager: ConnectivityManager) {
Expand All @@ -27,33 +29,29 @@ class ConnectivityListener(val connectivityManager: ConnectivityManager) {
val isConnected
get() = _isConnected.value

private lateinit var _currentDnsServers: StateFlow<List<InetAddress>>
private lateinit var _currentNetworkState: StateFlow<DaemonNetworkState?>

val currentDefaultNetworkState: DaemonNetworkState?
get() = _currentNetworkState.value

// Used by JNI
val currentDnsServers
get() = ArrayList(_currentDnsServers.value)
val currentDnsServers: ArrayList<InetAddress>
get() = _currentNetworkState.value?.dnsServers ?: ArrayList()

fun register(scope: CoroutineScope) {
_currentDnsServers =
dnsServerChanges().stateIn(scope, SharingStarted.Eagerly, currentDnsServers())
_currentNetworkState =
connectivityManager
.defaultNetworkStateFlow()
.map { it?.toDaemonNetworkState() }
.onEach { notifyDefaultNetworkChange(it) }
.stateIn(scope, SharingStarted.Eagerly, null)

_isConnected =
hasInternetCapability()
.onEach { notifyConnectivityChange(it) }
.stateIn(scope, SharingStarted.Eagerly, false)
}

private fun dnsServerChanges(): Flow<List<InetAddress>> =
connectivityManager
.defaultNetworkFlow()
.filterIsInstance<NetworkEvent.LinkPropertiesChanged>()
.onEach { Logger.d("Link properties changed") }
.map { it.linkProperties.dnsServersWithoutFallback() }

private fun currentDnsServers(): List<InetAddress> =
connectivityManager
.getLinkProperties(connectivityManager.activeNetwork)
?.dnsServersWithoutFallback() ?: emptyList()

private fun LinkProperties.dnsServersWithoutFallback(): List<InetAddress> =
dnsServers.filter { it.hostAddress != TalpidVpnService.FALLBACK_DUMMY_DNS_SERVER }

Expand Down Expand Up @@ -87,5 +85,14 @@ class ConnectivityListener(val connectivityManager: ConnectivityManager) {
.distinctUntilChanged()
}

private fun NetworkState.toDaemonNetworkState(): DaemonNetworkState =
DaemonNetworkState(
network.networkHandle,
linkProperties?.routes,
linkProperties?.dnsServersWithoutFallback(),
)

private external fun notifyConnectivityChange(isConnected: Boolean)

private external fun notifyDefaultNetworkChange(networkState: DaemonNetworkState?)
}
Loading
Loading