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() }