android, libtailscale: pass BuildConfig to Go code; use for DNS config

This commit wires up a method to allow the Tailscale Go backend to
obtain the build configuration, and then adds a new build configuration
to the build to control whether we fall back to the Google public DNS
servers if we can't determine the platform's DNS configuration.

This replaces the previous "IsPlayVersion" / "MaybeGoogle" check for
whether to use the DNS servers as fallbacks, to allow users to decide
this independently of what version of the Android app this is.

Updates tailscale/tailscale#13431

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
pull/495/head
Andrew Dunham 2 months ago
parent 283e1ebcd8
commit 45567146f4

@ -39,6 +39,14 @@ android {
targetSdkVersion 34 targetSdkVersion 34
versionCode 241 versionCode 241
versionName "1.73.114-t0970615b1-gab7ab737364" versionName "1.73.114-t0970615b1-gab7ab737364"
// This setting, which defaults to 'true', will cause Tailscale to fall
// back to the Google DNS servers if it cannot determine what the
// operating system's DNS configuration is.
//
// Set it to false either here or in your local.properties file to
// disable this behaviour.
buildConfigField "boolean", "USE_GOOGLE_DNS_FALLBACK", getLocalProperty("tailscale.useGoogleDnsFallback", "true")
} }
compileOptions { compileOptions {
@ -54,6 +62,7 @@ android {
jvmTarget = "17" jvmTarget = "17"
} }
buildFeatures { buildFeatures {
buildConfig true
compose true compose true
} }
composeOptions { composeOptions {
@ -66,9 +75,9 @@ android {
applicationTest { applicationTest {
initWith debug initWith debug
manifestPlaceholders.leanbackRequired = false manifestPlaceholders.leanbackRequired = false
buildConfigField "String", "GITHUB_USERNAME", "\"" + getLocalProperty("githubUsername")+"\"" buildConfigField "String", "GITHUB_USERNAME", "\"" + getLocalProperty("githubUsername", "")+"\""
buildConfigField "String", "GITHUB_PASSWORD", "\"" + getLocalProperty("githubPassword")+"\"" buildConfigField "String", "GITHUB_PASSWORD", "\"" + getLocalProperty("githubPassword", "")+"\""
buildConfigField "String", "GITHUB_2FA_SECRET", "\"" + getLocalProperty("github2FASecret")+"\"" buildConfigField "String", "GITHUB_2FA_SECRET", "\"" + getLocalProperty("github2FASecret", "")+"\""
} }
debug { debug {
manifestPlaceholders.leanbackRequired = false manifestPlaceholders.leanbackRequired = false
@ -156,12 +165,12 @@ dependencies {
implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.ui:ui-tooling-preview")
} }
def getLocalProperty(key) { def getLocalProperty(key, defaultValue) {
try { try {
Properties properties = new Properties() Properties properties = new Properties()
properties.load(project.file('local.properties').newDataInputStream()) properties.load(project.file('local.properties').newDataInputStream())
return properties.getProperty(key) return properties.getProperty(key) ?: defaultValue
} catch(Throwable ignored) { } catch(Throwable ignored) {
return "" return defaultValue
} }
} }

@ -38,6 +38,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import libtailscale.BuildConfig as GoBuildConfig
import libtailscale.Libtailscale import libtailscale.Libtailscale
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -311,6 +312,13 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
} }
} }
// getBuildConfig implements the libtailscale.AppContext interface.
override fun getBuildConfig(): GoBuildConfig {
var buildConfig = GoBuildConfig()
buildConfig.useGoogleDNSFallback = BuildConfig.USE_GOOGLE_DNS_FALLBACK
return buildConfig
}
fun notifyPolicyChanged() { fun notifyPolicyChanged() {
app.notifyPolicyChanged() app.notifyPolicyChanged()
} }

@ -44,6 +44,9 @@ type App struct {
// appCtx is a global reference to the com.tailscale.ipn.App instance. // appCtx is a global reference to the com.tailscale.ipn.App instance.
appCtx AppContext appCtx AppContext
// buildConfig is the build configuration for the app.
buildConfig *BuildConfig
store *stateStore store *stateStore
policyStore *syspolicyHandler policyStore *syspolicyHandler
logIDPublicAtomic atomic.Pointer[logid.PublicID] logIDPublicAtomic atomic.Pointer[logid.PublicID]
@ -98,6 +101,7 @@ type backend struct {
avoidEmptyDNS bool avoidEmptyDNS bool
appCtx AppContext appCtx AppContext
buildConfig *BuildConfig
} }
type settingsFunc func(*router.Config, *dns.OSConfig) error type settingsFunc func(*router.Config, *dns.OSConfig) error
@ -265,6 +269,7 @@ func (a *App) newBackend(dataDir, directFileRoot string, appCtx AppContext, stor
devices: newTUNDevices(), devices: newTUNDevices(),
settings: settings, settings: settings,
appCtx: appCtx, appCtx: appCtx,
buildConfig: a.buildConfig,
} }
var logID logid.PrivateID var logID logid.PrivateID
logID.UnmarshalText([]byte("dead0000dead0000dead0000dead0000dead0000dead0000dead0000dead0000")) logID.UnmarshalText([]byte("dead0000dead0000dead0000dead0000dead0000dead0000dead0000dead0000"))

@ -57,6 +57,12 @@ type AppContext interface {
// GetSyspolicyStringArrayValue returns the current string array value for the given system policy, // GetSyspolicyStringArrayValue returns the current string array value for the given system policy,
// expressed as a JSON string. // expressed as a JSON string.
GetSyspolicyStringArrayJSONValue(key string) (string, error) GetSyspolicyStringArrayJSONValue(key string) (string, error)
// GetBuildConfig gets the build configuration of the Android app.
//
// The returned BuildConfig should not change during the lifetime of
// the app.
GetBuildConfig() *BuildConfig
} }
// IPNService corresponds to our IPNService in Java. // IPNService corresponds to our IPNService in Java.
@ -166,3 +172,11 @@ func RequestVPN(service IPNService) {
func ServiceDisconnect(service IPNService) { func ServiceDisconnect(service IPNService) {
onDisconnect <- service onDisconnect <- service
} }
// BuildConfig is a struct that represents the build configuration of the
// Android application, as set in BuildConfig.java.
type BuildConfig struct {
// UseGoogleDNSFallback is whether to fall back to the Google public
// DNS servers if the platform's DNS servers cannot be determined.
UseGoogleDNSFallback bool
}

@ -287,7 +287,7 @@ func (b *backend) getDNSBaseConfig() (ret dns.OSConfig, _ error) {
// DNS config are lacking, and almost all Android phones use Google // DNS config are lacking, and almost all Android phones use Google
// services anyway, so it's a reasonable default: it's an ecosystem the // services anyway, so it's a reasonable default: it's an ecosystem the
// user has selected by having an Android device. // user has selected by having an Android device.
if len(ret.Nameservers) == 0 && b.appCtx.IsPlayVersion() { if len(ret.Nameservers) == 0 && b.buildConfig.UseGoogleDNSFallback {
log.Printf("getDNSBaseConfig: none found; falling back to Google public DNS") log.Printf("getDNSBaseConfig: none found; falling back to Google public DNS")
ret.Nameservers = append(ret.Nameservers, googleDNSServers...) ret.Nameservers = append(ret.Nameservers, googleDNSServers...)
} }

@ -38,6 +38,11 @@ func newApp(dataDir, directFileRoot string, appCtx AppContext) Application {
} }
a.ready.Add(2) a.ready.Add(2)
// Get the build configuration, if any.
if bc := appCtx.GetBuildConfig(); bc != nil {
a.buildConfig = bc
}
a.store = newStateStore(a.appCtx) a.store = newStateStore(a.appCtx)
a.policyStore = &syspolicyHandler{a: a} a.policyStore = &syspolicyHandler{a: a}
netmon.RegisterInterfaceGetter(a.getInterfaces) netmon.RegisterInterfaceGetter(a.getInterfaces)

Loading…
Cancel
Save