cmd/tailscale: fallback back to Google DNS on ChromeOS

Contrary to the VpnService.Builder documentation, ChromeOS doesn't
automatically fall back to the underlying network nameservers when
none are provided.

Updates tailscale/tailscale#431

Signed-off-by: Elias Naur <mail@eliasnaur.com>
pull/3/head
Elias Naur 4 years ago
parent 7211e6db1b
commit a7dfea267c

@ -139,5 +139,9 @@ public class App extends Application {
ft.commitNow();
}
boolean isChromeOS() {
return getPackageManager().hasSystemFeature("android.hardware.type.pc");
}
private static native void onConnectivityChanged(boolean connected);
}

@ -17,6 +17,7 @@ import (
"github.com/tailscale/wireguard-go/device"
"github.com/tailscale/wireguard-go/tun"
"golang.org/x/sys/unix"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/logtail"
"tailscale.com/logtail/filch"
@ -35,6 +36,10 @@ type backend struct {
settings func(*router.Config) error
lastCfg *router.Config
// avoidEmptyDNS controls whether to use fallback nameservers
// when no nameservers are provided by Tailscale.
avoidEmptyDNS bool
jvm jni.JVM
}
@ -54,6 +59,8 @@ const (
loginMethodWeb = "web"
)
var fallbackNameservers = []netaddr.IP{netaddr.IPv4(8, 8, 8, 8), netaddr.IPv4(8, 8, 4, 4)}
// errVPNNotPrepared is used when VPNService.Builder.establish returns
// null, either because the VPNService is not yet prepared or because
// VPN status was revoked.
@ -171,7 +178,11 @@ func (b *backend) updateTUN(service jni.Object, cfg *router.Config) error {
// builder.addDnsServer
addDnsServer := jni.GetMethodID(env, bcls, "addDnsServer", "(Ljava/lang/String;)Landroid/net/VpnService$Builder;")
for _, dns := range cfg.Nameservers {
nameservers := cfg.Nameservers
if b.avoidEmptyDNS && len(nameservers) == 0 {
nameservers = fallbackNameservers
}
for _, dns := range nameservers {
_, err = jni.CallObjectMethod(env,
builder,
addDnsServer,

@ -145,6 +145,12 @@ func (a *App) runBackend() error {
return err
}
defer b.CloseTUNs()
// Contrary to the documentation for VpnService.Builder.addDnsServer,
// ChromeOS doesn't fall back to the underlying network nameservers if
// we don't provide any.
b.avoidEmptyDNS = a.isChromeOS()
var timer *time.Timer
var alarmChan <-chan time.Time
alarm := func(t *time.Timer) {
@ -300,6 +306,21 @@ func (a *App) runBackend() error {
}
}
func (a *App) isChromeOS() bool {
var chromeOS bool
err := jni.Do(a.jvm, func(env jni.Env) error {
cls := jni.GetObjectClass(env, a.appCtx)
m := jni.GetMethodID(env, cls, "isChromeOS", "()Z")
b, err := jni.CallBooleanMethod(env, a.appCtx, m)
chromeOS = b
return err
})
if err != nil {
panic(err)
}
return chromeOS
}
// hostname builds a hostname from android.os.Build fields, in place of a
// useless os.Hostname().
func (a *App) hostname() string {

@ -11,5 +11,6 @@ require (
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e
inet.af/netaddr v0.0.0-20200718043157-99321d6ad24c
tailscale.com v0.100.1-0.20200724211229-0a42b0a72666
)

@ -17,6 +17,7 @@ __attribute__ ((visibility ("hidden"))) jint _jni_CallStaticIntMethodA(JNIEnv *e
__attribute__ ((visibility ("hidden"))) jobject _jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) void _jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) jobject _jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) jboolean _jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) jint _jni_CallIntMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) void _jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) jbyteArray _jni_NewByteArray(JNIEnv *env, jsize length);

@ -76,6 +76,10 @@ jobject _jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalu
return (*env)->CallObjectMethodA(env, obj, method, args);
}
jboolean _jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
return (*env)->CallBooleanMethodA(env, obj, method, args);
}
jint _jni_CallIntMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
return (*env)->CallIntMethodA(env, obj, method, args);
}

@ -117,6 +117,11 @@ func CallObjectMethod(e Env, obj Object, method MethodID, args ...Value) (Object
return Object(res), exception(e)
}
func CallBooleanMethod(e Env, obj Object, method MethodID, args ...Value) (bool, error) {
res := C._jni_CallBooleanMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args))
return res == C.JNI_TRUE, exception(e)
}
func CallIntMethod(e Env, obj Object, method MethodID, args ...Value) (int32, error) {
res := C._jni_CallIntMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args))
return int32(res), exception(e)

Loading…
Cancel
Save