oxtoacart/reactive_exit_node_bak
Percy Wegmann 2 years ago
parent a1e67ff1e9
commit dfd43a4195
No known key found for this signature in database
GPG Key ID: 29D8CDEB4C13D48B

@ -8,7 +8,6 @@ import com.tailscale.ipn.ui.model.BugReportID
import com.tailscale.ipn.ui.model.Errors import com.tailscale.ipn.ui.model.Errors
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.IpnLocal import com.tailscale.ipn.ui.model.IpnLocal
import com.tailscale.ipn.ui.model.IpnState
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -44,7 +43,6 @@ private object Endpoint {
const val TAILFS_SERVER_ADDRESS = "tailfs/fileserver-address" const val TAILFS_SERVER_ADDRESS = "tailfs/fileserver-address"
} }
typealias StatusResponseHandler = (Result<IpnState.Status>) -> Unit
typealias BugReportIdHandler = (Result<BugReportID>) -> Unit typealias BugReportIdHandler = (Result<BugReportID>) -> Unit
typealias PrefsHandler = (Result<Ipn.Prefs>) -> Unit typealias PrefsHandler = (Result<Ipn.Prefs>) -> Unit
@ -53,10 +51,6 @@ typealias PrefsHandler = (Result<Ipn.Prefs>) -> Unit
* corresponding method on this Client. * corresponding method on this Client.
*/ */
class Client(private val scope: CoroutineScope) { class Client(private val scope: CoroutineScope) {
fun status(responseHandler: StatusResponseHandler) {
get(Endpoint.STATUS, responseHandler = responseHandler)
}
fun bugReportId(responseHandler: BugReportIdHandler) { fun bugReportId(responseHandler: BugReportIdHandler) {
post(Endpoint.BUG_REPORT, responseHandler = responseHandler) post(Endpoint.BUG_REPORT, responseHandler = responseHandler)
} }

@ -1,30 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com.tailscale.ipn.ui.model
import kotlinx.serialization.Serializable
class Dns {
@Serializable
data class HostEntry(val addr: Addr?, val hosts: List<String>?)
@Serializable
data class OSConfig(
val hosts: List<HostEntry>? = null,
val nameservers: List<Addr>? = null,
val searchDomains: List<String>? = null,
val matchDomains: List<String>? = null,
) {
val isEmpty: Boolean
get() = (hosts.isNullOrEmpty()) &&
(nameservers.isNullOrEmpty()) &&
(searchDomains.isNullOrEmpty()) &&
(matchDomains.isNullOrEmpty())
}
}
class DnsType {
@Serializable
data class Resolver(var Addr: String? = null, var BootstrapResolution: List<Addr>? = null)
}

@ -9,13 +9,9 @@ class Ipn {
// Represents the overall state of the Tailscale engine. // Represents the overall state of the Tailscale engine.
enum class State(val value: Int) { enum class State(val value: Int) {
NoState(0), NoState(0), InUseOtherUser(1), NeedsLogin(2), NeedsMachineAuth(3), Stopped(4), Starting(5), Running(
InUseOtherUser(1), 6
NeedsLogin(2), );
NeedsMachineAuth(3),
Stopped(4),
Starting(5),
Running(6);
companion object { companion object {
fun fromInt(value: Int): State { fun fromInt(value: Int): State {
@ -34,13 +30,13 @@ class Ipn {
val FilesWaiting: Empty.Message? = null, val FilesWaiting: Empty.Message? = null,
val State: Int? = null, val State: Int? = null,
var Prefs: Prefs? = null, var Prefs: Prefs? = null,
var NetMap: Netmap.NetworkMap? = null, var NetMap: NetworkMap? = null,
var Engine: EngineStatus? = null, var Engine: EngineStatus? = null,
var BrowseToURL: String? = null, var BrowseToURL: String? = null,
var BackendLogId: String? = null, var BackendLogId: String? = null,
var LocalTCPPort: Int? = null, var LocalTCPPort: Int? = null,
var IncomingFiles: List<PartialFile>? = null, var IncomingFiles: List<PartialFile>? = null,
var ClientVersion: Tailcfg.ClientVersion? = null, // var ClientVersion: Tailcfg.ClientVersion? = null, // Currently unused
var TailFSShares: Map<String, String>? = null, var TailFSShares: Map<String, String>? = null,
) )
@ -133,7 +129,7 @@ class Ipn {
val RBytes: Long, val RBytes: Long,
val WBytes: Long, val WBytes: Long,
val NumLive: Int, val NumLive: Int,
val LivePeers: Map<String, IpnState.PeerStatusLite>, // val LivePeers: Map<String, IpnState.PeerStatusLite>,
) )
@Serializable @Serializable
@ -151,12 +147,9 @@ class Ipn {
class Persist { class Persist {
@Serializable @Serializable
data class Persist( data class Persist(
var PrivateMachineKey: String = var PrivateMachineKey: String = "privkey:0000000000000000000000000000000000000000000000000000000000000000",
"privkey:0000000000000000000000000000000000000000000000000000000000000000", var PrivateNodeKey: String = "privkey:0000000000000000000000000000000000000000000000000000000000000000",
var PrivateNodeKey: String = var OldPrivateNodeKey: String = "privkey:0000000000000000000000000000000000000000000000000000000000000000",
"privkey:0000000000000000000000000000000000000000000000000000000000000000",
var OldPrivateNodeKey: String =
"privkey:0000000000000000000000000000000000000000000000000000000000000000",
var Provider: String = "", var Provider: String = "",
) )
} }

@ -5,105 +5,6 @@ package com.tailscale.ipn.ui.model
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class IpnState {
@Serializable
data class PeerStatusLite(
val RxBytes: Long,
val TxBytes: Long,
val LastHandshake: String,
val NodeKey: String,
)
@Serializable
data class PeerStatus(
val ID: StableNodeID,
val HostName: String,
val DNSName: String,
val TailscaleIPs: List<Addr>? = null,
val Tags: List<String>? = null,
val PrimaryRoutes: List<String>? = null,
val Addrs: List<String>? = null,
val Online: Boolean,
val ExitNode: Boolean,
val ExitNodeOption: Boolean,
val Active: Boolean,
val PeerAPIURL: List<String>? = null,
val Capabilities: List<String>? = null,
val SSH_HostKeys: List<String>? = null,
val ShareeNode: Boolean? = null,
val Expired: Boolean? = null,
val Location: Tailcfg.Location? = null,
) {
fun computedName(status: Status): String {
val name = DNSName
val suffix = status.CurrentTailnet?.MagicDNSSuffix
suffix ?: return name
if (!(name.endsWith("." + suffix + "."))) {
return name
}
return name.dropLast(suffix.count() + 2)
}
}
@Serializable
data class ExitNodeStatus(
val ID: StableNodeID,
val Online: Boolean,
val TailscaleIPs: List<Prefix>? = null,
)
@Serializable
data class TailnetStatus(
val Name: String,
val MagicDNSSuffix: String,
val MagicDNSEnabled: Boolean,
)
@Serializable
data class Status(
val Version: String,
val TUN: Boolean,
val BackendState: String,
val AuthURL: String,
val TailscaleIPs: List<Addr>? = null,
val Self: PeerStatus? = null,
val ExitNodeStatus: ExitNodeStatus? = null,
val Health: List<String>? = null,
val CurrentTailnet: TailnetStatus? = null,
val CertDomains: List<String>? = null,
val Peer: Map<String, PeerStatus>? = null,
val User: Map<String, Tailcfg.UserProfile>? = null,
val ClientVersion: Tailcfg.ClientVersion? = null,
)
@Serializable
data class NetworkLockStatus(
var Enabled: Boolean,
var PublicKey: String,
var NodeKey: String,
var NodeKeySigned: Boolean,
var FilteredPeers: List<TKAFilteredPeer>? = null,
var StateID: ULong? = null,
)
@Serializable
data class TKAFilteredPeer(
var Name: String,
var TailscaleIPs: List<Addr>,
var NodeKey: String,
)
@Serializable
data class PingResult(
var IP: Addr,
var Err: String,
var LatencySeconds: Double,
)
}
class IpnLocal { class IpnLocal {
@Serializable @Serializable
data class LoginProfile( data class LoginProfile(

@ -5,17 +5,16 @@ package com.tailscale.ipn.ui.model
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class Netmap {
@Serializable @Serializable
data class NetworkMap( data class NetworkMap(
var SelfNode: Tailcfg.Node, var SelfNode: Tailcfg.Node,
var NodeKey: KeyNodePublic, // var NodeKey: KeyNodePublic, // Currently unused
var Peers: List<Tailcfg.Node>? = null, var Peers: List<Tailcfg.Node>? = null,
var Expiry: Time, // var Expiry: Time, // Currently unused
var Domain: String, // var Domain: String, // Currently unused
var UserProfiles: Map<String, Tailcfg.UserProfile>, var UserProfiles: Map<String, Tailcfg.UserProfile>,
var TKAEnabled: Boolean, // var TKAEnabled: Boolean, // Currently unused
var DNS: Tailcfg.DNSConfig? = null // var DNS: Tailcfg.DNSConfig? = null // Currently unused
) { ) {
// Keys are tailcfg.UserIDs thet get stringified // Keys are tailcfg.UserIDs thet get stringified
// Helpers // Helpers
@ -43,13 +42,12 @@ class Netmap {
if (other !is NetworkMap) return false if (other !is NetworkMap) return false
return SelfNode == other.SelfNode && return SelfNode == other.SelfNode &&
NodeKey == other.NodeKey && // NodeKey == other.NodeKey &&
Peers == other.Peers && Peers == other.Peers &&
Expiry == other.Expiry && // Expiry == other.Expiry &&
User() == other.User() && User() == other.User() &&
Domain == other.Domain && // Domain == other.Domain &&
UserProfiles == other.UserProfiles && UserProfiles == other.UserProfiles
TKAEnabled == other.TKAEnabled // TKAEnabled == other.TKAEnabled
}
} }
} }

@ -6,15 +6,16 @@ package com.tailscale.ipn.ui.model
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class Tailcfg { class Tailcfg {
@Serializable // Currently unused
data class ClientVersion( // @Serializable
var RunningLatest: Boolean? = null, // data class ClientVersion(
var LatestVersion: String? = null, // var RunningLatest: Boolean? = null,
var UrgentSecurityUpdate: Boolean? = null, // var LatestVersion: String? = null,
var Notify: Boolean? = null, // var UrgentSecurityUpdate: Boolean? = null,
var NotifyURL: String? = null, // var Notify: Boolean? = null,
var NotifyText: String? = null // var NotifyURL: String? = null,
) // var NotifyText: String? = null
// )
@Serializable @Serializable
data class UserProfile( data class UserProfile(
@ -30,55 +31,60 @@ class Tailcfg {
@Serializable @Serializable
data class Hostinfo( data class Hostinfo(
var IPNVersion: String? = null, // var IPNVersion: String? = null, // Currently unused
var FrontendLogID: String? = null, // var FrontendLogID: String? = null, // Currently unused
var BackendLogID: String? = null, // var BackendLogID: String? = null, // Currently unused
var OS: String? = null, var OS: String? = null,
var OSVersion: String? = null, // var OSVersion: String? = null, // Currently unused
var Env: String? = null, // var Env: String? = null, // Currently unused
var Distro: String? = null, // var Distro: String? = null, // Currently unused
var DistroVersion: String? = null, // var DistroVersion: String? = null, // Currently unused
var DistroCodeName: String? = null, // var DistroCodeName: String? = null, // Currently unused
var Desktop: Boolean? = null, // var Desktop: Boolean? = null, // Currently unused
var Package: String? = null, // var Package: String? = null, // Currently unused
var DeviceModel: String? = null, // var DeviceModel: String? = null, // Currently unused
var ShareeNode: Boolean? = null, // var ShareeNode: Boolean? = null, // Currently unused
var Hostname: String? = null, // var Hostname: String? = null, // Currently unused
var ShieldsUp: Boolean? = null, // var ShieldsUp: Boolean? = null, // Currently unused
var NoLogsNoSupport: Boolean? = null, // var NoLogsNoSupport: Boolean? = null, // Currently unused
var Machine: String? = null, // var Machine: String? = null, // Currently unused
var RoutableIPs: List<Prefix>? = null, // var RoutableIPs: List<Prefix>? = null, // Currently unused
var Services: List<Service>? = null, // var Services: List<Service>? = null, // Currently unused
var Location: Location? = null, var Location: Location? = null,
) )
@Serializable @Serializable
data class Node( data class Node(
var ID: NodeID, // var ID: NodeID, // Currently unused
var StableID: StableNodeID, var StableID: StableNodeID,
var Name: String, var Name: String,
var User: UserID, var User: UserID,
var Sharer: UserID? = null, // var Sharer: UserID? = null, // Currently unused
var Key: KeyNodePublic, // var Key: KeyNodePublic, // Currently unused
var KeyExpiry: String, var KeyExpiry: String,
var Machine: MachineKey, // var Machine: MachineKey, // Currently unused
var Addresses: List<Prefix>? = null, var Addresses: List<Prefix>? = null,
var AllowedIPs: List<Prefix>? = null, var AllowedIPs: List<Prefix>? = null,
var Endpoints: List<String>? = null, // var Endpoints: List<String>? = null, // Currently unused
var Hostinfo: Hostinfo, var Hostinfo: Hostinfo,
var Created: Time, // var Created: Time, // Currently unused
var LastSeen: Time? = null, // var LastSeen: Time? = null, // Currently unused
var Online: Boolean? = null, var Online: Boolean? = null,
var Capabilities: List<String>? = null, var Capabilities: List<String>? = null,
var ComputedName: String, var ComputedName: String,
var ComputedNameWithHost: String // var ComputedNameWithHost: String // Currently unused
) { ) {
val isAdmin: Boolean val isAdmin: Boolean
get() = (Capabilities ?: emptyList()).contains("https://tailscale.com/cap/is-admin") get() = (Capabilities ?: emptyList()).contains("https://tailscale.com/cap/is-admin")
// isExitNode reproduces the Go logic in local.go peerStatusFromNode
val isExitNode: Boolean =
AllowedIPs?.contains("0.0.0.0/0") ?: false && AllowedIPs?.contains("::/0") ?: false
} }
@Serializable // Currently unused
data class Service(var Proto: String, var Port: Int, var Description: String? = null) // @Serializable
// data class Service(var Proto: String, var Port: Int, var Description: String? = null)
@Serializable @Serializable
data class NetworkProfile(var MagicDNSName: String? = null, var DomainName: String? = null) data class NetworkProfile(var MagicDNSName: String? = null, var DomainName: String? = null)
@ -88,16 +94,17 @@ class Tailcfg {
var Country: String? = null, var Country: String? = null,
var CountryCode: String? = null, var CountryCode: String? = null,
var City: String? = null, var City: String? = null,
var CityCode: String? = null, // var CityCode: String? = null, // Currently unused
var Priority: Int? = null var Priority: Int? = null
) )
@Serializable // Currently unused
data class DNSConfig( // @Serializable
var Resolvers: List<DnsType.Resolver>? = null, // data class DNSConfig(
var Routes: Map<String, List<DnsType.Resolver>?>? = null, // var Resolvers: List<DnsType.Resolver>? = null,
var FallbackResolvers: List<DnsType.Resolver>? = null, // var Routes: Map<String, List<DnsType.Resolver>?>? = null,
var Domains: List<String>? = null, // var FallbackResolvers: List<DnsType.Resolver>? = null,
var Nameservers: List<Addr>? = null // var Domains: List<String>? = null,
) // var Nameservers: List<Addr>? = null
// )
} }

@ -7,11 +7,11 @@ import kotlinx.serialization.Serializable
typealias Addr = String typealias Addr = String
typealias Prefix = String typealias Prefix = String
typealias NodeID = Long //typealias NodeID = Long // Currently unused
typealias KeyNodePublic = String //typealias KeyNodePublic = String // Currently unused
typealias MachineKey = String //typealias MachineKey = String // Currently unused
typealias UserID = Long typealias UserID = Long
typealias Time = String //typealias Time = String // Currently unused
typealias StableNodeID = String typealias StableNodeID = String
typealias BugReportID = String typealias BugReportID = String

@ -6,7 +6,7 @@ package com.tailscale.ipn.ui.notifier
import android.util.Log import android.util.Log
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Ipn.Notify import com.tailscale.ipn.ui.model.Ipn.Notify
import com.tailscale.ipn.ui.model.Netmap import com.tailscale.ipn.ui.model.NetworkMap
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -32,7 +32,7 @@ object Notifier {
private val isReady = CompletableDeferred<Boolean>() private val isReady = CompletableDeferred<Boolean>()
val state: StateFlow<Ipn.State> = MutableStateFlow(Ipn.State.NoState) val state: StateFlow<Ipn.State> = MutableStateFlow(Ipn.State.NoState)
val netmap: StateFlow<Netmap.NetworkMap?> = MutableStateFlow(null) val netmap: StateFlow<NetworkMap?> = MutableStateFlow(null)
val prefs: StateFlow<Ipn.Prefs?> = MutableStateFlow(null) val prefs: StateFlow<Ipn.Prefs?> = MutableStateFlow(null)
val engineStatus: StateFlow<Ipn.EngineStatus?> = MutableStateFlow(null) val engineStatus: StateFlow<Ipn.EngineStatus?> = MutableStateFlow(null)
val tailFSShares: StateFlow<Map<String, String>?> = MutableStateFlow(null) val tailFSShares: StateFlow<Map<String, String>?> = MutableStateFlow(null)

@ -4,7 +4,7 @@
package com.tailscale.ipn.ui.util package com.tailscale.ipn.ui.util
import com.tailscale.ipn.ui.model.Netmap import com.tailscale.ipn.ui.model.NetworkMap
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.model.UserID import com.tailscale.ipn.ui.model.UserID
import com.tailscale.ipn.ui.notifier.Notifier import com.tailscale.ipn.ui.notifier.Notifier
@ -37,7 +37,7 @@ class PeerCategorizer(scope: CoroutineScope) {
} }
} }
private fun regenerateGroupedPeers(netmap: Netmap.NetworkMap): List<PeerSet> { private fun regenerateGroupedPeers(netmap: NetworkMap): List<PeerSet> {
val peers: List<Tailcfg.Node> = netmap.Peers ?: return emptyList() val peers: List<Tailcfg.Node> = netmap.Peers ?: return emptyList()
val selfNode = netmap.SelfNode val selfNode = netmap.SelfNode
var grouped = mutableMapOf<UserID, MutableList<Tailcfg.Node>>() var grouped = mutableMapOf<UserID, MutableList<Tailcfg.Node>>()

@ -4,17 +4,20 @@
package com.tailscale.ipn.ui.viewModel package com.tailscale.ipn.ui.viewModel
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tailscale.ipn.ui.localapi.Client import com.tailscale.ipn.ui.localapi.Client
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.StableNodeID import com.tailscale.ipn.ui.model.StableNodeID
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.LoadingIndicator
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.util.TreeMap import java.util.TreeMap
data class ExitNodePickerNav( data class ExitNodePickerNav(
@ -52,22 +55,22 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel
val anyActive: StateFlow<Boolean> = MutableStateFlow(false) val anyActive: StateFlow<Boolean> = MutableStateFlow(false)
init { init {
Client(viewModelScope).status { result -> viewModelScope.launch {
result.onFailure { Notifier.netmap.combine(Notifier.prefs) { netmap, prefs -> Pair(netmap, prefs) }
Log.e(TAG, "getStatus: ${it.message}") .stateIn(viewModelScope).collect { (netmap, prefs) ->
}.onSuccess { val exitNodeId = prefs?.ExitNodeID
it.Peer?.values?.let { peers -> netmap?.Peers?.let { peers ->
val allNodes = peers.filter { it.ExitNodeOption }.map { val allNodes = peers.filter { it.isExitNode }.map {
ExitNode( ExitNode(
id = it.ID, id = it.StableID,
label = it.DNSName, label = it.Name,
online = it.Online, online = it.Online ?: false,
selected = it.ExitNode, selected = it.StableID == exitNodeId,
mullvad = it.DNSName.endsWith(".mullvad.ts.net."), mullvad = it.Name.endsWith(".mullvad.ts.net."),
priority = it.Location?.Priority ?: 0, priority = it.Hostinfo?.Location?.Priority ?: 0,
countryCode = it.Location?.CountryCode ?: "", countryCode = it.Hostinfo?.Location?.CountryCode ?: "",
country = it.Location?.Country ?: "", country = it.Hostinfo?.Location?.Country ?: "",
city = it.Location?.City ?: "", city = it.Hostinfo?.Location?.City ?: "",
) )
} }

Loading…
Cancel
Save