diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-06-14 15:13:29 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-06-14 15:13:29 +0200 |
| commit | 4c999e3a422b933934f93950473a669cf8afb805 (patch) | |
| tree | c160ac08bbbb8e0a342dc57720ccd33a0a94dbc2 /android/lib/daemon-grpc/src | |
| parent | 6fb4623503fda3df77a9c7c3cc152a45506ba1a4 (diff) | |
| parent | cef76db82f08abf08b59624a87e6991ba4e5e39a (diff) | |
| download | mullvadvpn-4c999e3a422b933934f93950473a669cf8afb805.tar.xz mullvadvpn-4c999e3a422b933934f93950473a669cf8afb805.zip | |
Merge branch 'android-api-access-method'
Diffstat (limited to 'android/lib/daemon-grpc/src')
3 files changed, 204 insertions, 2 deletions
diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt index f8073323c8..987b7e56ea 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt @@ -3,6 +3,8 @@ package net.mullvad.mullvadvpn.lib.daemon.grpc import android.net.LocalSocketAddress import android.util.Log import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure import arrow.optics.copy import arrow.optics.dsl.index import arrow.optics.typeclasses.Index @@ -42,7 +44,11 @@ import net.mullvad.mullvadvpn.lib.daemon.grpc.util.LogInterceptor import net.mullvad.mullvadvpn.lib.daemon.grpc.util.connectivityFlow import net.mullvad.mullvadvpn.lib.model.AccountData import net.mullvad.mullvadvpn.lib.model.AccountNumber +import net.mullvad.mullvadvpn.lib.model.AddApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.AddSplitTunnelingAppError +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.AppId import net.mullvad.mullvadvpn.lib.model.AppVersionInfo as ModelAppVersionInfo import net.mullvad.mullvadvpn.lib.model.ClearAllOverridesError @@ -67,6 +73,7 @@ import net.mullvad.mullvadvpn.lib.model.GetAccountHistoryError import net.mullvad.mullvadvpn.lib.model.GetDeviceListError import net.mullvad.mullvadvpn.lib.model.GetDeviceStateError import net.mullvad.mullvadvpn.lib.model.LoginAccountError +import net.mullvad.mullvadvpn.lib.model.NewAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings import net.mullvad.mullvadvpn.lib.model.Ownership as ModelOwnership import net.mullvad.mullvadvpn.lib.model.PlayPurchase @@ -84,9 +91,11 @@ import net.mullvad.mullvadvpn.lib.model.RelayItemId as ModelRelayItemId import net.mullvad.mullvadvpn.lib.model.RelayList as ModelRelayList import net.mullvad.mullvadvpn.lib.model.RelayList import net.mullvad.mullvadvpn.lib.model.RelaySettings +import net.mullvad.mullvadvpn.lib.model.RemoveApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.RemoveSplitTunnelingAppError import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation import net.mullvad.mullvadvpn.lib.model.SetAllowLanError +import net.mullvad.mullvadvpn.lib.model.SetApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.SetAutoConnectError import net.mullvad.mullvadvpn.lib.model.SetDnsOptionsError import net.mullvad.mullvadvpn.lib.model.SetObfuscationOptionsError @@ -96,8 +105,11 @@ import net.mullvad.mullvadvpn.lib.model.SetWireguardMtuError import net.mullvad.mullvadvpn.lib.model.SetWireguardQuantumResistantError import net.mullvad.mullvadvpn.lib.model.Settings as ModelSettings import net.mullvad.mullvadvpn.lib.model.SettingsPatchError +import net.mullvad.mullvadvpn.lib.model.TestApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.TunnelState as ModelTunnelState +import net.mullvad.mullvadvpn.lib.model.UnknownApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.UnknownCustomListError +import net.mullvad.mullvadvpn.lib.model.UpdateApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.UpdateCustomListError import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken import net.mullvad.mullvadvpn.lib.model.WireguardConstraints as ModelWireguardConstraints @@ -161,6 +173,10 @@ class ManagementService( val wireguardEndpointData: Flow<ModelWireguardEndpointData> = relayList.mapNotNull { it.wireguardEndpointData } + private val _mutableCurrentAccessMethod = MutableStateFlow<ApiAccessMethodSetting?>(null) + val currentAccessMethod: Flow<ApiAccessMethodSetting> = + _mutableCurrentAccessMethod.filterNotNull() + fun start() { // Just to ensure that connection is set up since the connection won't be setup without a // call to the daemon @@ -196,9 +212,11 @@ class ManagementService( _mutableVersionInfo.update { event.versionInfo.toDomain() } ManagementInterface.DaemonEvent.EventCase.DEVICE -> _mutableDeviceState.update { event.device.newState.toDomain() } + ManagementInterface.DaemonEvent.EventCase.NEW_ACCESS_METHOD -> { + _mutableCurrentAccessMethod.update { event.newAccessMethod.toDomain() } + } ManagementInterface.DaemonEvent.EventCase.REMOVE_DEVICE -> {} ManagementInterface.DaemonEvent.EventCase.EVENT_NOT_SET -> {} - ManagementInterface.DaemonEvent.EventCase.NEW_ACCESS_METHOD -> {} } } } @@ -297,6 +315,7 @@ class ManagementService( async { _mutableSettings.update { getSettings() } }, async { _mutableVersionInfo.update { getVersionInfo() } }, async { _mutableRelayList.update { getRelayList() } }, + async { _mutableCurrentAccessMethod.update { getCurrentApiAccessMethod() } } ) } } @@ -572,6 +591,55 @@ class ManagementService( Either.catch { grpc.getWwwAuthToken(Empty.getDefaultInstance()) } .map { WebsiteAuthToken.fromString(it.value) } + suspend fun addApiAccessMethod( + newAccessMethodSetting: NewAccessMethodSetting + ): Either<AddApiAccessMethodError, ApiAccessMethodId> = + Either.catch { grpc.addApiAccessMethod(newAccessMethodSetting.fromDomain()) } + .mapLeft(AddApiAccessMethodError::Unknown) + .map { ApiAccessMethodId.fromString(it.value) } + + suspend fun removeApiAccessMethod( + apiAccessMethodId: ApiAccessMethodId + ): Either<RemoveApiAccessMethodError, Unit> = + Either.catch { grpc.removeApiAccessMethod(apiAccessMethodId.fromDomain()) } + .mapLeft(RemoveApiAccessMethodError::Unknown) + .mapEmpty() + + suspend fun setApiAccessMethod( + apiAccessMethodId: ApiAccessMethodId + ): Either<SetApiAccessMethodError, Unit> = + Either.catch { grpc.setApiAccessMethod(apiAccessMethodId.fromDomain()) } + .mapLeft(SetApiAccessMethodError::Unknown) + .mapEmpty() + + suspend fun updateApiAccessMethod( + apiAccessMethodSetting: ApiAccessMethodSetting + ): Either<UpdateApiAccessMethodError, Unit> = + Either.catch { grpc.updateApiAccessMethod(apiAccessMethodSetting.fromDomain()) } + .mapLeft(::UnknownApiAccessMethodError) + .mapEmpty() + + private suspend fun getCurrentApiAccessMethod(): ApiAccessMethodSetting = + grpc.getCurrentApiAccessMethod(Empty.getDefaultInstance()).toDomain() + + suspend fun testCustomApiAccessMethod( + customProxy: ApiAccessMethod.CustomProxy + ): Either<TestApiAccessMethodError, Unit> = + Either.catch { grpc.testCustomApiAccessMethod(customProxy.fromDomain()) } + .mapLeftStatus { TestApiAccessMethodError.Grpc } + .map { result -> + either { ensure(result.value) { TestApiAccessMethodError.CouldNotAccess } } + } + + suspend fun testApiAccessMethodById( + apiAccessMethodId: ApiAccessMethodId + ): Either<TestApiAccessMethodError, Unit> = + Either.catch { grpc.testApiAccessMethodById(apiAccessMethodId.fromDomain()) } + .mapLeftStatus { TestApiAccessMethodError.Grpc } + .map { result -> + either { ensure(result.value) { TestApiAccessMethodError.CouldNotAccess } } + } + private fun <A> Either<A, Empty>.mapEmpty() = map {} private inline fun <B, C> Either<Throwable, B>.mapLeftStatus( diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/FromDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/FromDomain.kt index 4efd8c452e..014bafb85b 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/FromDomain.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/FromDomain.kt @@ -1,6 +1,9 @@ package net.mullvad.mullvadvpn.lib.daemon.grpc.mapper import mullvad_daemon.management_interface.ManagementInterface +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.CustomDnsOptions import net.mullvad.mullvadvpn.lib.model.CustomList @@ -9,6 +12,7 @@ import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions import net.mullvad.mullvadvpn.lib.model.DnsOptions import net.mullvad.mullvadvpn.lib.model.DnsState import net.mullvad.mullvadvpn.lib.model.GeoLocationId +import net.mullvad.mullvadvpn.lib.model.NewAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.PlayPurchase @@ -18,6 +22,8 @@ import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItemId import net.mullvad.mullvadvpn.lib.model.RelaySettings import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation +import net.mullvad.mullvadvpn.lib.model.SocksAuth +import net.mullvad.mullvadvpn.lib.model.TransportProtocol import net.mullvad.mullvadvpn.lib.model.Udp2TcpObfuscationSettings import net.mullvad.mullvadvpn.lib.model.WireguardConstraints @@ -160,3 +166,74 @@ internal fun PlayPurchase.fromDomain(): ManagementInterface.PlayPurchase = .setPurchaseToken(purchaseToken.fromDomain()) .setProductId(productId) .build() + +internal fun NewAccessMethodSetting.fromDomain(): ManagementInterface.NewAccessMethodSetting = + ManagementInterface.NewAccessMethodSetting.newBuilder() + .setName(name.value) + .setEnabled(enabled) + .setAccessMethod( + ManagementInterface.AccessMethod.newBuilder().setCustom(apiAccessMethod.fromDomain()) + ) + .build() + +internal fun ApiAccessMethod.fromDomain(): ManagementInterface.AccessMethod = + ManagementInterface.AccessMethod.newBuilder() + .let { + when (this) { + ApiAccessMethod.Direct -> + it.setDirect(ManagementInterface.AccessMethod.Direct.getDefaultInstance()) + ApiAccessMethod.Bridges -> + it.setBridges(ManagementInterface.AccessMethod.Bridges.getDefaultInstance()) + is ApiAccessMethod.CustomProxy -> it.setCustom(this.fromDomain()) + } + } + .build() + +internal fun ApiAccessMethod.CustomProxy.fromDomain(): ManagementInterface.CustomProxy = + ManagementInterface.CustomProxy.newBuilder() + .let { + when (this) { + is ApiAccessMethod.CustomProxy.Shadowsocks -> it.setShadowsocks(this.fromDomain()) + is ApiAccessMethod.CustomProxy.Socks5Remote -> it.setSocks5Remote(this.fromDomain()) + } + } + .build() + +internal fun ApiAccessMethod.CustomProxy.Socks5Remote.fromDomain(): + ManagementInterface.Socks5Remote = + ManagementInterface.Socks5Remote.newBuilder().setIp(ip).setPort(port.value).let { + auth?.let { auth -> it.setAuth(auth.fromDomain()) } + it.build() + } + +internal fun SocksAuth.fromDomain(): ManagementInterface.SocksAuth = + ManagementInterface.SocksAuth.newBuilder().setUsername(username).setPassword(password).build() + +internal fun ApiAccessMethod.CustomProxy.Shadowsocks.fromDomain(): ManagementInterface.Shadowsocks = + ManagementInterface.Shadowsocks.newBuilder() + .setIp(ip) + .setCipher(cipher.label) + .setPort(port.value) + .let { + if (password != null) { + it.setPassword(password) + } + it.build() + } + +internal fun TransportProtocol.fromDomain(): ManagementInterface.TransportProtocol = + when (this) { + TransportProtocol.Tcp -> ManagementInterface.TransportProtocol.TCP + TransportProtocol.Udp -> ManagementInterface.TransportProtocol.UDP + } + +internal fun ApiAccessMethodId.fromDomain(): ManagementInterface.UUID = + ManagementInterface.UUID.newBuilder().setValue(value.toString()).build() + +internal fun ApiAccessMethodSetting.fromDomain(): ManagementInterface.AccessMethodSetting = + ManagementInterface.AccessMethodSetting.newBuilder() + .setName(name.value) + .setId(id.fromDomain()) + .setEnabled(enabled) + .setAccessMethod(apiAccessMethod.fromDomain()) + .build() diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt index 59a94f62dc..13ebe74350 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt @@ -13,8 +13,13 @@ import net.mullvad.mullvadvpn.lib.model.AccountData import net.mullvad.mullvadvpn.lib.model.AccountId import net.mullvad.mullvadvpn.lib.model.AccountNumber import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName +import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.AppId import net.mullvad.mullvadvpn.lib.model.AppVersionInfo +import net.mullvad.mullvadvpn.lib.model.Cipher import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.CustomDnsOptions import net.mullvad.mullvadvpn.lib.model.CustomList @@ -53,6 +58,7 @@ import net.mullvad.mullvadvpn.lib.model.RelayOverride import net.mullvad.mullvadvpn.lib.model.RelaySettings import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation import net.mullvad.mullvadvpn.lib.model.Settings +import net.mullvad.mullvadvpn.lib.model.SocksAuth import net.mullvad.mullvadvpn.lib.model.SplitTunnelSettings import net.mullvad.mullvadvpn.lib.model.TransportProtocol import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint @@ -246,7 +252,8 @@ internal fun ManagementInterface.Settings.toDomain(): Settings = tunnelOptions = tunnelOptions.toDomain(), relayOverrides = relayOverridesList.map { it.toDomain() }, showBetaReleases = showBetaReleases, - splitTunnelSettings = splitTunnel.toDomain() + splitTunnelSettings = splitTunnel.toDomain(), + apiAccessMethodSettings = apiAccessMethods.toDomain() ) internal fun ManagementInterface.RelayOverride.toDomain(): RelayOverride = @@ -519,3 +526,53 @@ internal fun ManagementInterface.SplitTunnelSettings.toDomain(): SplitTunnelSett internal fun ManagementInterface.PlayPurchasePaymentToken.toDomain(): PlayPurchasePaymentToken = PlayPurchasePaymentToken(value = token) + +internal fun ManagementInterface.ApiAccessMethodSettings.toDomain(): List<ApiAccessMethodSetting> = + listOf(direct.toDomain(), mullvadBridges.toDomain()).plus(customList.map { it.toDomain() }) + +internal fun ManagementInterface.AccessMethodSetting.toDomain(): ApiAccessMethodSetting = + ApiAccessMethodSetting( + id = ApiAccessMethodId.fromString(id.value), + name = ApiAccessMethodName.fromString(name), + enabled = enabled, + apiAccessMethod = accessMethod.toDomain() + ) + +internal fun ManagementInterface.AccessMethod.toDomain(): ApiAccessMethod = + when { + hasDirect() -> ApiAccessMethod.Direct + hasBridges() -> ApiAccessMethod.Bridges + hasCustom() -> custom.toDomain() + else -> error("Type not found") + } + +internal fun ManagementInterface.CustomProxy.toDomain(): ApiAccessMethod.CustomProxy = + when { + hasShadowsocks() -> shadowsocks.toDomain() + hasSocks5Remote() -> socks5Remote.toDomain() + hasSocks5Local() -> error("Socks5 local not supported") + else -> error("Custom proxy not found") + } + +internal fun ManagementInterface.Shadowsocks.toDomain(): ApiAccessMethod.CustomProxy.Shadowsocks = + ApiAccessMethod.CustomProxy.Shadowsocks( + ip = ip, + port = Port(port), + password = password, + cipher = Cipher.fromString(cipher) + ) + +internal fun ManagementInterface.Socks5Remote.toDomain(): ApiAccessMethod.CustomProxy.Socks5Remote = + ApiAccessMethod.CustomProxy.Socks5Remote( + ip = ip, + port = Port(port), + auth = + if (hasAuth()) { + auth.toDomain() + } else { + null + } + ) + +internal fun ManagementInterface.SocksAuth.toDomain(): SocksAuth = + SocksAuth(username = username, password = password) |
