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 5 years ago
parent 7211e6db1b
commit a7dfea267c

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

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

@ -145,6 +145,12 @@ func (a *App) runBackend() error {
return err return err
} }
defer b.CloseTUNs() 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 timer *time.Timer
var alarmChan <-chan time.Time var alarmChan <-chan time.Time
alarm := func(t *time.Timer) { 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 // hostname builds a hostname from android.os.Build fields, in place of a
// useless os.Hostname(). // useless os.Hostname().
func (a *App) hostname() string { func (a *App) hostname() string {

@ -11,5 +11,6 @@ require (
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e 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 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"))) 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"))) 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"))) 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"))) 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"))) void _jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args);
__attribute__ ((visibility ("hidden"))) jbyteArray _jni_NewByteArray(JNIEnv *env, jsize length); __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); 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) { jint _jni_CallIntMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
return (*env)->CallIntMethodA(env, obj, method, 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) 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) { 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)) res := C._jni_CallIntMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args))
return int32(res), exception(e) return int32(res), exception(e)

Loading…
Cancel
Save