From 88a5d3c140b829d3a832fd002887477317f9f6a1 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 28 May 2025 10:11:59 -0400 Subject: [PATCH] android: modify mullvad exit node detection logic (#656) updates tailscale/corp#29045 We ran into an issue where the current detection logic was not sufficient to filter out mullvad nodes. This modifies the logic so we scan both the Name and ComputedName for the mullvad domain and also treat all nodes with location info as mullvad nodes. While all of these conditions *should* be true for any mullvad node, in practice it's possible that they aren't so we or them together for some redundancy and define a mullvad exit node to be any node where any of these conditions is true. Signed-off-by: Jonathan Nobels --- .../src/main/java/com/tailscale/ipn/ui/model/TailCfg.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/model/TailCfg.kt b/android/src/main/java/com/tailscale/ipn/ui/model/TailCfg.kt index a51579e..2e9be75 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/model/TailCfg.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/model/TailCfg.kt @@ -15,9 +15,9 @@ import com.tailscale.ipn.ui.util.DisplayAddress import com.tailscale.ipn.ui.util.TimeUtil import com.tailscale.ipn.ui.util.flag import com.tailscale.ipn.ui.viewModel.PeerSettingInfo -import java.util.Date import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement +import java.util.Date class Tailcfg { @Serializable @@ -107,8 +107,13 @@ class Tailcfg { val isExitNode: Boolean = (AllowedIPs?.contains("0.0.0.0/0") ?: false) && (AllowedIPs?.contains("::/0") ?: false) + // mullvad nodes are exit nodes with a mullvad.ts.net domain *or* Location Info. + // These checks are intentionally redundant to avoid false negatives. val isMullvadNode: Boolean - get() = Name.endsWith(".mullvad.ts.net.") + get() = + Name.endsWith(".mullvad.ts.net") || + ComputedName?.endsWith(".mullvad.ts.net") == true || + Hostinfo.Location != null val displayName: String get() = ComputedName ?: Name