diff --git a/config/config.go b/config/config.go index 71d468c0..c1e8505e 100644 --- a/config/config.go +++ b/config/config.go @@ -41,6 +41,7 @@ type General struct { IPv6 bool `json:"ipv6"` Interface string `json:"-"` RoutingMark int `json:"-"` + Tun Tun `json:"tun"` } // Inbound config diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 3641c750..de7530d8 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -114,6 +114,7 @@ func GetGeneral() *config.General { Mode: tunnel.Mode(), LogLevel: log.Level(), IPv6: !resolver.DisableIPv6, + Tun: P.GetTunConf(), } return general diff --git a/hub/route/configs.go b/hub/route/configs.go index a930c32b..c9a4e833 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -2,6 +2,7 @@ package route import ( "net/http" + "net/netip" "path/filepath" "github.com/Dreamacro/clash/component/resolver" @@ -31,12 +32,20 @@ type configSchema struct { TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` MitmPort *int `json:"mitm-port"` - Tun *config.Tun `json:"tun"` AllowLan *bool `json:"allow-lan"` BindAddress *string `json:"bind-address"` Mode *tunnel.TunnelMode `json:"mode"` LogLevel *log.LogLevel `json:"log-level"` IPv6 *bool `json:"ipv6"` + Tun *tunConfigSchema `json:"tun"` +} + +type tunConfigSchema struct { + Enable *bool `json:"enable"` + Device *string `json:"device"` + Stack *constant.TUNStack `json:"stack"` + DNSHijack *[]netip.AddrPort `json:"dns-hijack"` + AutoRoute *bool `json:"auto-route"` } func getConfigs(w http.ResponseWriter, r *http.Request) { @@ -92,6 +101,29 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { resolver.DisableIPv6 = !*general.IPv6 } + if general.Tun != nil { + tunSchema := general.Tun + tunConf := P.GetTunConf() + + if tunSchema.Enable != nil { + tunConf.Enable = *tunSchema.Enable + } + if tunSchema.Device != nil { + tunConf.Device = *tunSchema.Device + } + if tunSchema.Stack != nil { + tunConf.Stack = *tunSchema.Stack + } + if tunSchema.DNSHijack != nil { + tunConf.DNSHijack = *tunSchema.DNSHijack + } + if tunSchema.AutoRoute != nil { + tunConf.AutoRoute = *tunSchema.AutoRoute + } + + P.ReCreateTun(&tunConf, nil, tcpIn, udpIn) + } + render.NoContent(w, r) } diff --git a/listener/listener.go b/listener/listener.go index bda55e71..b7396c68 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -8,6 +8,7 @@ import ( "net" "net/netip" "os" + "sort" "strconv" "sync" "time" @@ -31,8 +32,10 @@ import ( ) var ( - allowLan = false - bindAddress = "*" + allowLan = false + bindAddress = "*" + lastTunConf *config.Tun + lastTunAddressPrefix *netip.Prefix socksListener *socks.Listener socksUDPListener *socks.UDPListener @@ -65,6 +68,15 @@ type Ports struct { MitmPort int `json:"mitm-port"` } +func GetTunConf() config.Tun { + if lastTunConf == nil { + return config.Tun{ + Enable: false, + } + } + return *lastTunConf +} + func AllowLan() bool { return allowLan } @@ -328,13 +340,22 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan defer func() { if err != nil { log.Errorln("Start TUN listening error: %s", err.Error()) - os.Exit(2) } }() + if tunAddressPrefix == nil { + tunAddressPrefix = lastTunAddressPrefix + } + + if !hasTunConfigChange(tunConf, tunAddressPrefix) { + return + } + if tunStackListener != nil { - tunStackListener.Close() + _ = tunStackListener.Close() tunStackListener = nil + lastTunConf = nil + lastTunAddressPrefix = nil } if !tunConf.Enable { @@ -342,6 +363,12 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan } tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn) + if err != nil { + return + } + + lastTunConf = tunConf + lastTunAddressPrefix = tunAddressPrefix } func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) { @@ -482,6 +509,47 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } +func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool { + if lastTunConf == nil { + return true + } + + if len(lastTunConf.DNSHijack) != len(tunConf.DNSHijack) { + return true + } + + sort.Slice(lastTunConf.DNSHijack, func(i, j int) bool { + return lastTunConf.DNSHijack[i].Addr().Less(lastTunConf.DNSHijack[j].Addr()) + }) + + sort.Slice(tunConf.DNSHijack, func(i, j int) bool { + return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr()) + }) + + for i, dns := range tunConf.DNSHijack { + if dns != lastTunConf.DNSHijack[i] { + return true + } + } + + if lastTunConf.Enable != tunConf.Enable || + lastTunConf.Device != tunConf.Device || + lastTunConf.Stack != tunConf.Stack || + lastTunConf.AutoRoute != tunConf.AutoRoute { + return true + } + + if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) { + return true + } + + if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix { + return true + } + + return false +} + func initCert() error { if _, err := os.Stat(C.Path.RootCA()); os.IsNotExist(err) { log.Infoln("Can't find mitm_ca.crt, start generate")