Skip to content

Commit 9987dbc

Browse files
authored
android: only update DNS configs on LinkProperties changes (#511)
We were updating DNS configs when capabilities changed, without LinkProperties having been filled in. Because onAvailable always happened first, LinkProperties were created with default value, and onCapabilitiesChanged sent a DNS update using those LinkProperties. This change only updates DNS configs on LinkProperties, which is the last update sent on a network change. Updates tailscale/tailscale#13173 Signed-off-by: kari-ts <[email protected]>
1 parent 8b91b0f commit 9987dbc

File tree

1 file changed

+28
-28
lines changed

1 file changed

+28
-28
lines changed

android/src/main/java/com/tailscale/ipn/NetworkChangeCallback.kt

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,33 @@ import android.net.NetworkCapabilities
99
import android.net.NetworkRequest
1010
import android.util.Log
1111
import libtailscale.Libtailscale
12-
import java.net.InetAddress
1312
import java.util.concurrent.locks.ReentrantLock
1413
import kotlin.concurrent.withLock
1514

1615
object NetworkChangeCallback {
1716

1817
private const val TAG = "NetworkChangeCallback"
1918

20-
private data class NetworkInfo(
21-
var caps: NetworkCapabilities,
22-
var linkProps: LinkProperties
23-
)
19+
private data class NetworkInfo(var caps: NetworkCapabilities, var linkProps: LinkProperties)
2420

2521
private val lock = ReentrantLock()
22+
2623
private val activeNetworks = mutableMapOf<Network, NetworkInfo>() // keyed by Network
2724

2825
// monitorDnsChanges sets up a network callback to monitor changes to the
2926
// system's network state and update the DNS configuration when interfaces
3027
// become available or properties of those interfaces change.
3128
fun monitorDnsChanges(connectivityManager: ConnectivityManager, dns: DnsConfig) {
3229
val networkConnectivityRequest =
33-
NetworkRequest.Builder()
34-
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
35-
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
36-
.build()
30+
NetworkRequest.Builder()
31+
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
32+
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
33+
.build()
3734

3835
// Use registerNetworkCallback to listen for updates from all networks, and
39-
// then update DNS configs for the best network.
36+
// then update DNS configs for the best network when LinkProperties are changed.
37+
// Per
38+
// https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network), this happens after all other updates.
4039
//
4140
// Note that we can't use registerDefaultNetworkCallback because the
4241
// default network used by Tailscale will always show up with capability
@@ -51,23 +50,19 @@ object NetworkChangeCallback {
5150
Log.d(TAG, "onAvailable: network ${network}")
5251
lock.withLock {
5352
activeNetworks[network] = NetworkInfo(NetworkCapabilities(), LinkProperties())
54-
maybeUpdateDNSConfigLocked("onAvailable", dns)
5553
}
5654
}
5755

5856
override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) {
5957
super.onCapabilitiesChanged(network, capabilities)
60-
lock.withLock {
61-
activeNetworks[network]?.caps = capabilities
62-
maybeUpdateDNSConfigLocked("onCapabilitiesChanged", dns)
63-
}
58+
lock.withLock { activeNetworks[network]?.caps = capabilities }
6459
}
6560

6661
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
6762
super.onLinkPropertiesChanged(network, linkProperties)
6863
lock.withLock {
6964
activeNetworks[network]?.linkProps = linkProperties
70-
maybeUpdateDNSConfigLocked("onLinkPropertiesChanged", dns)
65+
maybeUpdateDNSConfig("onLinkPropertiesChanged", dns)
7166
}
7267
}
7368

@@ -77,7 +72,7 @@ object NetworkChangeCallback {
7772
Log.d(TAG, "onLost: network ${network}")
7873
lock.withLock {
7974
activeNetworks.remove(network)
80-
maybeUpdateDNSConfigLocked("onLost", dns)
75+
maybeUpdateDNSConfig("onLost", dns)
8176
}
8277
}
8378
})
@@ -101,11 +96,12 @@ object NetworkChangeCallback {
10196
// Filter the list of all networks to those that have the INTERNET
10297
// capability, are not VPNs, and have a non-zero number of DNS servers
10398
// available.
104-
val networks = activeNetworks.filter { (_, info) ->
105-
info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
106-
info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) &&
107-
info.linkProps.dnsServers.isNotEmpty() == true
108-
}
99+
val networks =
100+
activeNetworks.filter { (_, info) ->
101+
info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
102+
info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) &&
103+
info.linkProps.dnsServers.isNotEmpty() == true
104+
}
109105

110106
// If we have one; just return it; otherwise, prefer networks that are also
111107
// not metered (i.e. cell modems).
@@ -122,7 +118,9 @@ object NetworkChangeCallback {
122118
for ((network, info) in activeNetworks) {
123119
if (info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
124120
info.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
125-
Log.w(TAG, "no networks available that also have DNS servers set; falling back to first network ${network}")
121+
Log.w(
122+
TAG,
123+
"no networks available that also have DNS servers set; falling back to first network ${network}")
126124
return network
127125
}
128126
}
@@ -136,17 +134,17 @@ object NetworkChangeCallback {
136134

137135
// maybeUpdateDNSConfig will maybe update our DNS configuration based on the
138136
// current set of active Networks.
139-
//
140-
// 'lock' must be held.
141-
private fun maybeUpdateDNSConfigLocked(why: String, dns: DnsConfig) {
137+
private fun maybeUpdateDNSConfig(why: String, dns: DnsConfig) {
142138
val defaultNetwork = pickDefaultNetwork()
143139
if (defaultNetwork == null) {
144140
Log.d(TAG, "${why}: no default network available; not updating DNS config")
145141
return
146142
}
147143
val info = activeNetworks[defaultNetwork]
148144
if (info == null) {
149-
Log.w(TAG, "${why}: [unexpected] no info available for default network; not updating DNS config")
145+
Log.w(
146+
TAG,
147+
"${why}: [unexpected] no info available for default network; not updating DNS config")
150148
return
151149
}
152150

@@ -160,7 +158,9 @@ object NetworkChangeCallback {
160158
sb.append(searchDomains)
161159
}
162160
if (dns.updateDNSFromNetwork(sb.toString())) {
163-
Log.d(TAG, "${why}: updated DNS config for network ${defaultNetwork} (${info.linkProps.interfaceName})")
161+
Log.d(
162+
TAG,
163+
"${why}: updated DNS config for network ${defaultNetwork} (${info.linkProps.interfaceName})")
164164
Libtailscale.onDNSConfigChanged(info.linkProps.interfaceName)
165165
}
166166
}

0 commit comments

Comments
 (0)