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 <andrea@gottardo.me>
angott/dot-915
Andrea Gottardo 1 year ago
parent 1465b2a67f
commit 0df6a1dbe0

@ -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<String> {
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<String> {
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

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

Loading…
Cancel
Save