From 0df6a1dbe0c247190a3857e23ace3a0652d8fbab Mon Sep 17 00:00:00 2001 From: Andrea Gottardo Date: Wed, 24 Jul 2024 11:44:12 -0700 Subject: [PATCH] android: add DoT server(s) IP address to VPNBuilder routes Updates tailscale/tailscale#915 This is a first attempt at diagnosing what is preventing DNS queries from resolving on Android whenever a custom DoT configuration is specified in the system settings. We observed that Android is attempting to reach the DoT server using the Tailscale interface, however we were not setting the proper routes to allow these queries to go through. This is a first step and it requires a change in the backend because at the moment the following error is thrown when DoT traffic attempts to leave the device through the Tailscale interface: ``` 34.7M/343.4M open-conn-track: timeout opening (TCP 100.77.253.30:49652 => 188.172.213.149:853); no associated peer node ``` Signed-off-by: Andrea Gottardo --- .../src/main/java/com/tailscale/ipn/App.kt | 28 ++++++++++++++++++- libtailscale/net.go | 18 ++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/tailscale/ipn/App.kt b/android/src/main/java/com/tailscale/ipn/App.kt index 85e512a..5df82e9 100644 --- a/android/src/main/java/com/tailscale/ipn/App.kt +++ b/android/src/main/java/com/tailscale/ipn/App.kt @@ -181,6 +181,23 @@ class App : UninitializedApp(), libtailscale.AppContext { sb.append(searchDomains) } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val isUsingPrivateDNS = linkProperties?.isPrivateDnsActive == true + if (isUsingPrivateDNS) { + Log.d( + "App", "Using private DNS, extracting validated DoT DNS server(s) IP addresses") + linkProperties?.let { + val stringDescription = it.toString() + val dotServersIPAddresses = extractPrivateDNSIPAddresses(stringDescription) + Log.d("App", "Private DNS IP addresses: $dotServersIPAddresses") + if (dotServersIPAddresses.isNotEmpty()) { + sb.append("\n") + sb.append(dotServersIPAddresses.joinToString(",")) + } + } + } + } + if (dns.updateDNSFromNetwork(sb.toString())) { Libtailscale.onDNSConfigChanged(linkProperties?.interfaceName) } @@ -195,6 +212,14 @@ class App : UninitializedApp(), libtailscale.AppContext { }) } + private fun extractPrivateDNSIPAddresses(stringDescription: String): List { + val regex = "ValidatedPrivateDnsAddresses: \\[(.*?)]".toRegex() + val matchResult = regex.find(stringDescription) + val ipAddressString = matchResult?.groupValues?.get(1) + + return ipAddressString?.split(",")?.map { it.trim() } ?: emptyList() + } + // encryptToPref a byte array of data using the Jetpack Security // library and writes it to a global encrypted preference store. @Throws(IOException::class, GeneralSecurityException::class) @@ -510,7 +535,8 @@ open class UninitializedApp : Application() { } fun disallowedPackageNames(): List { - val mdmDisallowed = MDMSettings.excludedPackages.flow.value?.split(",")?.map { it.trim() } ?: emptyList() + val mdmDisallowed = + MDMSettings.excludedPackages.flow.value?.split(",")?.map { it.trim() } ?: emptyList() if (mdmDisallowed.isNotEmpty()) { Log.d(TAG, "Excluded application packages were set via MDM: $mdmDisallowed") return builtInDisallowedPackageNames + mdmDisallowed diff --git a/libtailscale/net.go b/libtailscale/net.go index ec3f60f..6486cdd 100644 --- a/libtailscale/net.go +++ b/libtailscale/net.go @@ -171,6 +171,14 @@ func (b *backend) updateTUN(service IPNService, rcfg *router.Config, dcfg *dns.O } } + for _, dotIPAddr := range b.getDoTServerAddresses() { + b.logger.Logf("updateTUN: adding DoT server %s", dotIPAddr) + if err := builder.AddRoute(dotIPAddr, 32); err != nil { + b.logger.Logf("updateTUN: failed to add DoT server %s: %v", dotIPAddr, err) + return err + } + } + for _, route := range rcfg.LocalRoutes { addr := route.Addr() if addr.IsLoopback() { @@ -292,6 +300,16 @@ func (b *backend) getDNSBaseConfig() (ret dns.OSConfig, _ error) { return config, nil } +// getDoTServerAddresses returns the DoT server addresses by extracting them from the string defining the Android DNS config. +func (b *backend) getDoTServerAddresses() []string { + str := b.appCtx.GetPlatformDNSConfig() + lines := strings.Split(str, "\n") + if len(lines) < 3 { + return []string{} + } + return strings.Split(strings.Trim(lines[2], " \n"), ",") +} + func (b *backend) getPlatformDNSConfig() string { return b.appCtx.GetPlatformDNSConfig() }