|
|
@@ -5,9 +5,12 @@ import (
|
|
|
"context"
|
|
|
go_errors "errors"
|
|
|
"fmt"
|
|
|
+ "os"
|
|
|
+ "runtime"
|
|
|
"sort"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
|
"github.com/xtls/xray-core/common/errors"
|
|
|
@@ -191,7 +194,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
|
|
|
}
|
|
|
|
|
|
if s.checkSystem {
|
|
|
- supportIPv4, supportIPv6 := checkSystemNetwork()
|
|
|
+ supportIPv4, supportIPv6 := checkRoutes()
|
|
|
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
|
|
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
|
|
} else {
|
|
|
@@ -328,21 +331,66 @@ func init() {
|
|
|
}))
|
|
|
}
|
|
|
|
|
|
-func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
|
|
|
- conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
|
|
|
- if err4 != nil {
|
|
|
- supportIPv4 = false
|
|
|
- } else {
|
|
|
- supportIPv4 = true
|
|
|
- conn4.Close()
|
|
|
+func probeRoutes() (ipv4 bool, ipv6 bool) {
|
|
|
+ if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil {
|
|
|
+ ipv4 = true
|
|
|
+ conn.Close()
|
|
|
}
|
|
|
-
|
|
|
- conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
|
|
|
- if err6 != nil {
|
|
|
- supportIPv6 = false
|
|
|
- } else {
|
|
|
- supportIPv6 = true
|
|
|
- conn6.Close()
|
|
|
+ if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
|
|
|
+ ipv6 = true
|
|
|
+ conn.Close()
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+var routeCache struct {
|
|
|
+ sync.Once
|
|
|
+ sync.RWMutex
|
|
|
+ expire time.Time
|
|
|
+ ipv4, ipv6 bool
|
|
|
+}
|
|
|
+
|
|
|
+func checkRoutes() (bool, bool) {
|
|
|
+ if !isGUIPlatform {
|
|
|
+ routeCache.Once.Do(func() {
|
|
|
+ routeCache.ipv4, routeCache.ipv6 = probeRoutes()
|
|
|
+ })
|
|
|
+ return routeCache.ipv4, routeCache.ipv6
|
|
|
+ }
|
|
|
+
|
|
|
+ routeCache.RWMutex.RLock()
|
|
|
+ now := time.Now()
|
|
|
+ if routeCache.expire.After(now) {
|
|
|
+ routeCache.RWMutex.RUnlock()
|
|
|
+ return routeCache.ipv4, routeCache.ipv6
|
|
|
+ }
|
|
|
+ routeCache.RWMutex.RUnlock()
|
|
|
+
|
|
|
+ routeCache.RWMutex.Lock()
|
|
|
+ defer routeCache.RWMutex.Unlock()
|
|
|
+
|
|
|
+ now = time.Now()
|
|
|
+ if routeCache.expire.After(now) { // double-check
|
|
|
+ return routeCache.ipv4, routeCache.ipv6
|
|
|
+ }
|
|
|
+ routeCache.ipv4, routeCache.ipv6 = probeRoutes() // ~2ms
|
|
|
+ routeCache.expire = now.Add(100 * time.Millisecond) // ttl
|
|
|
+ return routeCache.ipv4, routeCache.ipv6
|
|
|
+}
|
|
|
+
|
|
|
+var isGUIPlatform = detectGUIPlatform()
|
|
|
+
|
|
|
+func detectGUIPlatform() bool {
|
|
|
+ switch runtime.GOOS {
|
|
|
+ case "android", "ios", "windows", "darwin":
|
|
|
+ return true
|
|
|
+ case "linux", "freebsd", "openbsd":
|
|
|
+ if t := os.Getenv("XDG_SESSION_TYPE"); t == "wayland" || t == "x11" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|