summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-04-04 08:49:59 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-04-04 09:53:14 +0200
commit637af3c22b57b9fc06e74c1ca2c34952aecc1f70 (patch)
treeed91e4e8ce381d83d98885a277895e9e5ab64b97 /android
parent119925c7887f735c688eaf491ea64d9e1b80b0c5 (diff)
downloadmullvadvpn-637af3c22b57b9fc06e74c1ca2c34952aecc1f70.tar.xz
mullvadvpn-637af3c22b57b9fc06e74c1ca2c34952aecc1f70.zip
Show correct in ip when using multihop
Diffstat (limited to 'android')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt118
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt1
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt22
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt1
5 files changed, 139 insertions, 9 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
index 43bc448805..4b5ea29fd6 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
@@ -482,6 +482,7 @@ class ConnectScreenTest {
// In
every { mockTunnelEndpoint.obfuscation } returns null
+ every { mockTunnelEndpoint.entryEndpoint } returns null
every { mockTunnelEndpoint.endpoint.address.address.hostAddress } returns inHost
every { mockTunnelEndpoint.endpoint.address.port } returns inPort
every { mockTunnelEndpoint.endpoint.protocol } returns inProtocol
@@ -683,4 +684,121 @@ class ConnectScreenTest {
verify(exactly = 1) { onAccountClickMockk() }
}
}
+
+ @Test
+ fun showConnectionDetailsObfuscation() {
+ composeExtension.use {
+ // Arrange
+ val mockLocation: GeoIpLocation = mockk(relaxed = true)
+ val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true)
+ val mockHostName = "Host-Name"
+ val inHost = "1.1.1.1"
+ val inPort = 99
+ val inProtocol = TransportProtocol.Tcp
+ every { mockLocation.hostname } returns mockHostName
+ every { mockLocation.entryHostname } returns null
+
+ // In
+ every {
+ mockTunnelEndpoint.obfuscation?.endpoint?.address?.address?.hostAddress
+ } returns inHost
+ every { mockTunnelEndpoint.obfuscation?.endpoint?.address?.port } returns inPort
+ every { mockTunnelEndpoint.obfuscation?.endpoint?.protocol } returns inProtocol
+
+ // Out Ipv4
+ val outIpv4 = "ipv4address"
+ every { mockLocation.ipv4?.hostAddress } returns outIpv4
+
+ // Out Ipv6
+ val outIpv6 = "ipv6address"
+ every { mockLocation.ipv6?.hostAddress } returns outIpv6
+
+ initScreen(
+ state =
+ ConnectUiState(
+ location = mockLocation,
+ selectedRelayItemTitle = null,
+ tunnelState =
+ TunnelState.Connected(mockTunnelEndpoint, mockLocation, emptyList()),
+ showLocation = false,
+ deviceName = "",
+ daysLeftUntilExpiry = null,
+ inAppNotification = null,
+ isPlayBuild = false,
+ )
+ )
+
+ // Act
+ onNodeWithTag(CONNECT_CARD_HEADER_TEST_TAG).performClick()
+
+ // Assert
+ onNodeWithText(mockHostName).assertExists()
+ onNodeWithText("In").assertExists()
+ onNodeWithText("$inHost:$inPort TCP").assertExists()
+
+ onNodeWithText("Out IPv4").assertExists()
+ onNodeWithText(outIpv4).assertExists()
+
+ onNodeWithText("Out IPv6").assertExists()
+ onNodeWithText(outIpv6).assertExists()
+ }
+ }
+
+ @Test
+ fun showConnectionDetailsMultihop() {
+ composeExtension.use {
+ // Arrange
+ val mockLocation: GeoIpLocation = mockk(relaxed = true)
+ val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true)
+ val mockHostName = "Host-Name"
+ val inHost = "8.8.8.8"
+ val inPort = 55
+ val inProtocol = TransportProtocol.Udp
+ every { mockLocation.hostname } returns mockHostName
+ every { mockLocation.entryHostname } returns null
+
+ // In
+ every { mockTunnelEndpoint.obfuscation } returns null
+ every { mockTunnelEndpoint.entryEndpoint?.address?.address?.hostAddress } returns inHost
+ every { mockTunnelEndpoint.entryEndpoint?.address?.port } returns inPort
+ every { mockTunnelEndpoint.entryEndpoint?.protocol } returns inProtocol
+
+ // Out Ipv4
+ val outIpv4 = "ipv4address"
+ every { mockLocation.ipv4?.hostAddress } returns outIpv4
+
+ // Out Ipv6
+ val outIpv6 = "ipv6address"
+ every { mockLocation.ipv6?.hostAddress } returns outIpv6
+
+ initScreen(
+ state =
+ ConnectUiState(
+ location = mockLocation,
+ selectedRelayItemTitle = null,
+ tunnelState =
+ TunnelState.Connected(mockTunnelEndpoint, mockLocation, emptyList()),
+ showLocation = false,
+ deviceName = "",
+ daysLeftUntilExpiry = null,
+ inAppNotification = null,
+ isPlayBuild = false,
+ )
+ )
+
+ // Act
+ onNodeWithTag(CONNECT_CARD_HEADER_TEST_TAG).performClick()
+
+ // Assert
+ onNodeWithText(mockHostName).assertExists()
+ onNodeWithText("In").assertExists()
+ onNodeWithText("$inHost:$inPort UDP").assertExists()
+
+ onNodeWithText("Out IPv4").assertExists()
+ onNodeWithText(outIpv4).assertExists()
+
+ onNodeWithText("Out IPv6").assertExists()
+ onNodeWithText(outIpv6).assertExists()
+ }
+ }
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt
index 796cd0ca6a..b13d153cc2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt
@@ -192,7 +192,11 @@ fun ConnectionDetails(
@Composable
fun TunnelEndpoint.toInAddress(): String {
- val relayEndpoint = this.obfuscation?.endpoint ?: this.endpoint
+ // Order is important
+ // First we check for obfuscation (Shadowsocks, UDP-Over-UDP)
+ // Then we check for entry if we have multihop
+ // Finally we check for exit endpoint
+ val relayEndpoint = obfuscation?.endpoint ?: entryEndpoint ?: endpoint
val host = relayEndpoint.address.address.hostAddress ?: ""
val port = relayEndpoint.address.port
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt
index bfb9ed40f8..a35de85e4c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt
@@ -40,6 +40,7 @@ object TunnelStatePreviewData {
private fun generateTunnelEndpoint(quantumResistant: Boolean, daita: Boolean): TunnelEndpoint =
TunnelEndpoint(
+ entryEndpoint = null,
endpoint = generateEndpoint(TransportProtocol.Udp),
quantumResistant = quantumResistant,
obfuscation =
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 5e2f7fb831..40ee4d6bcf 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
@@ -176,16 +176,15 @@ internal fun ManagementInterface.GeoIpLocation.toDomain(): GeoIpLocation =
internal fun ManagementInterface.TunnelEndpoint.toDomain(): TunnelEndpoint =
TunnelEndpoint(
endpoint =
- with(address) {
- val indexOfSeparator = indexOfLast { it == ':' }
- val ipPart =
- address.substring(0, indexOfSeparator).filter { it !in listOf('[', ']') }
- val portPart = address.substring(indexOfSeparator + 1)
-
+ Endpoint(address = address.toInetSocketAddress(), protocol = protocol.toDomain()),
+ entryEndpoint =
+ if (hasEntryEndpoint()) {
Endpoint(
- address = InetSocketAddress(InetAddress.getByName(ipPart), portPart.toInt()),
- protocol = protocol.toDomain(),
+ address = entryEndpoint.address.toInetSocketAddress(),
+ protocol = entryEndpoint.protocol.toDomain(),
)
+ } else {
+ null
},
quantumResistant = quantumResistant,
obfuscation =
@@ -204,6 +203,13 @@ internal fun ManagementInterface.ObfuscationEndpoint.toDomain(): ObfuscationEndp
obfuscationType = obfuscationType.toDomain(),
)
+private fun String.toInetSocketAddress(): InetSocketAddress {
+ val indexOfSeparator = indexOfLast { it == ':' }
+ val ipPart = substring(0, indexOfSeparator).filter { it !in listOf('[', ']') }
+ val portPart = substring(indexOfSeparator + 1)
+ return InetSocketAddress(InetAddress.getByName(ipPart), portPart.toInt())
+}
+
internal fun ManagementInterface.ObfuscationEndpoint.ObfuscationType.toDomain(): ObfuscationType =
when (this) {
ManagementInterface.ObfuscationEndpoint.ObfuscationType.UDP2TCP -> ObfuscationType.Udp2Tcp
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt
index 3902e5c965..0092b4a09c 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.lib.model
data class TunnelEndpoint(
+ val entryEndpoint: Endpoint?,
val endpoint: Endpoint,
val quantumResistant: Boolean,
val obfuscation: ObfuscationEndpoint?,