From b15344ec78367058df471e2fd0a6f051d63ae6a0 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 18 Jan 2022 21:09:36 +0800 Subject: [PATCH] [Refactor] 1.allow maybe empty group 2.use COMPATIBLE(DIRECT alias) when proxy group is empty 3.http provider pass through tunnel --- adapter/inbound/socket.go | 13 ++++++++++ adapter/outbound/direct.go | 10 ++++++++ adapter/outboundgroup/common.go | 14 ++++++++-- adapter/outboundgroup/fallback.go | 5 ++-- adapter/outboundgroup/loadbalance.go | 4 ++- adapter/outboundgroup/relay.go | 6 +++-- adapter/outboundgroup/selector.go | 3 ++- adapter/outboundgroup/urltest.go | 6 +++-- adapter/provider/provider.go | 16 ++++++------ adapter/provider/vehicle.go | 5 ++-- config/config.go | 27 +------------------- constant/adapters.go | 5 ++-- constant/metadata.go | 7 +++++ hub/executor/executor.go | 38 ++++++++++++++++++++++++++-- listener/inner/tcp.go | 20 +++++++++++++++ listener/listener.go | 2 ++ rule/provider.go | 4 +++ 17 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 listener/inner/tcp.go diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index be717017e..f761bd9ba 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -20,3 +20,16 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo return context.NewConnContext(conn, metadata) } + +func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { + metadata := &C.Metadata{} + metadata.NetWork = C.TCP + metadata.Type = C.INNER + metadata.DNSMode = C.DNSMapping + metadata.AddrType = C.AtypDomainName + metadata.Host = host + if _, port, err := parseAddr(dst); err == nil { + metadata.DstPort = port + } + return context.NewConnContext(conn, metadata) +} diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 4c4305f57..4cf16e38c 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -44,3 +44,13 @@ func NewDirect() *Direct { }, } } + +func NewCompatible() *Direct { + return &Direct{ + Base: &Base{ + name: "COMPATIBLE", + tp: C.Compatible, + udp: true, + }, + } +} diff --git a/adapter/outboundgroup/common.go b/adapter/outboundgroup/common.go index 1c9333b9d..ab988f558 100644 --- a/adapter/outboundgroup/common.go +++ b/adapter/outboundgroup/common.go @@ -1,6 +1,7 @@ package outboundgroup import ( + "github.com/Dreamacro/clash/tunnel" "regexp" "time" @@ -21,6 +22,7 @@ func getProvidersProxies(providers []provider.ProxyProvider, touch bool, filter proxies = append(proxies, provider.Proxies()...) } } + var filterReg *regexp.Regexp = nil matchedProxies := []C.Proxy{} if len(filter) > 0 { @@ -32,8 +34,16 @@ func getProvidersProxies(providers []provider.ProxyProvider, touch bool, filter } //if no proxy matched, means bad filter, return all proxies if len(matchedProxies) > 0 { - proxies = matchedProxies + return matchedProxies + } else { + return append([]C.Proxy{}, tunnel.Proxies()["COMPATIBLE"]) + } + } else { + if len(proxies) == 0 { + return append(proxies, tunnel.Proxies()["COMPATIBLE"]) + } else { + return proxies } } - return proxies + } diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 734bd6b78..b795229b0 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -72,7 +72,7 @@ func (f *Fallback) onDialFailed() { } else { failedCount := f.failedTimes.Inc() log.Warnln("%s failed count: %d", f.Name(), failedCount) - if failedCount >= 5 && failedCount < 6 { + if failedCount >= 5 { log.Warnln("because %s failed multiple times, active health check", f.Name()) for _, proxyProvider := range f.providers { go proxyProvider.HealthCheck() @@ -97,10 +97,11 @@ func (f *Fallback) SupportUDP() bool { // MarshalJSON implements C.ProxyAdapter func (f *Fallback) MarshalJSON() ([]byte, error) { - var all []string + all := make([]string, 0) for _, proxy := range f.proxies(false) { all = append(all, proxy.Name()) } + return json.Marshal(map[string]interface{}{ "type": f.Type().String(), "now": f.Now(), diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 2a6e3b302..402062c8f 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -150,10 +150,12 @@ func (lb *LoadBalance) proxies(touch bool) []C.Proxy { // MarshalJSON implements C.ProxyAdapter func (lb *LoadBalance) MarshalJSON() ([]byte, error) { - var all []string + all := make([]string, 0) + for _, proxy := range lb.proxies(false) { all = append(all, proxy.Name()) } + return json.Marshal(map[string]interface{}{ "type": lb.Type().String(), "all": all, diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index fab24ed4a..f794afe32 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -23,7 +23,7 @@ type Relay struct { func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { var proxies []C.Proxy for _, proxy := range r.proxies(metadata, true) { - if proxy.Type() != C.Direct { + if proxy.Type() != C.Direct && proxy.Type() != C.Compatible { proxies = append(proxies, proxy) } } @@ -69,10 +69,12 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d // MarshalJSON implements C.ProxyAdapter func (r *Relay) MarshalJSON() ([]byte, error) { - var all []string + all := make([]string, 0) + for _, proxy := range r.rawProxies(false) { all = append(all, proxy.Name()) } + return json.Marshal(map[string]interface{}{ "type": r.Type().String(), "all": all, diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index d79e4a92c..47b8d34f8 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -50,7 +50,8 @@ func (s *Selector) SupportUDP() bool { // MarshalJSON implements C.ProxyAdapter func (s *Selector) MarshalJSON() ([]byte, error) { - var all []string + all := make([]string, 0) + for _, proxy := range getProvidersProxies(s.providers, false, s.filter) { all = append(all, proxy.Name()) } diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 836dcaac8..28a9fe407 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -125,10 +125,12 @@ func (u *URLTest) SupportUDP() bool { // MarshalJSON implements C.ProxyAdapter func (u *URLTest) MarshalJSON() ([]byte, error) { - var all []string + all := make([]string, 0) + for _, proxy := range u.proxies(false) { all = append(all, proxy.Name()) } + return json.Marshal(map[string]interface{}{ "type": u.Type().String(), "now": u.Now(), @@ -149,7 +151,7 @@ func (u *URLTest) onDialFailed() { } else { failedCount := u.failedTimes.Inc() log.Warnln("%s failed count: %d", u.Name(), failedCount) - if failedCount >= 5 && failedCount < 6 { + if failedCount >= 5 { log.Warnln("because %s failed multiple times, active health check", u.Name()) for _, proxyProvider := range u.providers { go proxyProvider.HealthCheck() diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 6e27d2ccc..49aceda02 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -67,6 +67,10 @@ func (pp *proxySetProvider) Initial() error { } pp.onUpdate(elm) + if pp.healthCheck.auto() { + go pp.healthCheck.process() + } + return nil } @@ -102,10 +106,6 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh return nil, fmt.Errorf("invalid filter regex: %w", err) } - if hc.auto() { - go hc.process() - } - pd := &proxySetProvider{ proxies: []C.Proxy{}, healthCheck: hc, @@ -190,6 +190,10 @@ func (cp *compatibleProvider) Update() error { } func (cp *compatibleProvider) Initial() error { + if cp.healthCheck.auto() { + go cp.healthCheck.process() + } + return nil } @@ -219,10 +223,6 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co return nil, errors.New("provider need one proxy at least") } - if hc.auto() { - go hc.process() - } - pd := &compatibleProvider{ name: name, proxies: proxies, diff --git a/adapter/provider/vehicle.go b/adapter/provider/vehicle.go index a0237a923..e2efa46ec 100644 --- a/adapter/provider/vehicle.go +++ b/adapter/provider/vehicle.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/Dreamacro/clash/listener/inner" "io" "net" "net/http" @@ -10,7 +11,6 @@ import ( "time" netHttp "github.com/Dreamacro/clash/common/net" - "github.com/Dreamacro/clash/component/dialer" types "github.com/Dreamacro/clash/constant/provider" ) @@ -77,7 +77,8 @@ func (h *HTTPVehicle) Read() ([]byte, error) { TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { - return dialer.DialContext(ctx, network, address) + conn := inner.HandleTcp(address, uri.Hostname()) + return conn, nil }, } diff --git a/config/config.go b/config/config.go index 740d0a4ce..1a2d218f9 100644 --- a/config/config.go +++ b/config/config.go @@ -350,6 +350,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect()) proxies["REJECT"] = adapter.NewProxy(outbound.NewReject()) + proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible()) proxyList = append(proxyList, "DIRECT", "REJECT") // parse proxy @@ -396,13 +397,6 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ providersMap[name] = pd } - for _, rp := range providersMap { - log.Infoln("Start initial provider %s", rp.Name()) - if err := rp.Initial(); err != nil { - return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", rp.Name(), err) - } - } - // parse proxy group for idx, mapping := range groupsConfig { group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap) @@ -418,18 +412,6 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ proxies[groupName] = adapter.NewProxy(group) } - // initial compatible provider - for _, pd := range providersMap { - if pd.VehicleType() != providerTypes.Compatible { - continue - } - - log.Infoln("Start initial compatible provider %s", pd.Name()) - if err := pd.Initial(); err != nil { - return nil, nil, err - } - } - var ps []C.Proxy for _, v := range proxyList { ps = append(ps, proxies[v]) @@ -511,13 +493,6 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin R.SetRuleProvider(rp) } - for _, ruleProvider := range ruleProviders { - log.Infoln("Start initial provider %s", (*ruleProvider).Name()) - if err := (*ruleProvider).Initial(); err != nil { - return nil, nil, fmt.Errorf("initial rule provider %s error: %w", (*ruleProvider).Name(), err) - } - } - var rules []C.Rule rulesConfig := cfg.Rule mode := cfg.Mode diff --git a/constant/adapters.go b/constant/adapters.go index c152bc7b6..e460e41f8 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -13,7 +13,7 @@ import ( const ( Direct AdapterType = iota Reject - + Compatible Shadowsocks ShadowsocksR Snell @@ -128,7 +128,8 @@ func (at AdapterType) String() string { return "Direct" case Reject: return "Reject" - + case Compatible: + return "Compatible" case Shadowsocks: return "Shadowsocks" case ShadowsocksR: diff --git a/constant/metadata.go b/constant/metadata.go index f14b93f36..73a499738 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -24,6 +24,7 @@ const ( REDIR TPROXY TUN + INNER ) type NetWork int @@ -59,6 +60,8 @@ func (t Type) String() string { return "TProxy" case TUN: return "Tun" + case INNER: + return "Inner" default: return "Unknown" } @@ -94,6 +97,10 @@ func (m *Metadata) SourceDetail() string { if m.Process != "" { return fmt.Sprintf("%s(%s)", m.SourceAddress(), m.Process) } else { + if m.Type == INNER { + return fmt.Sprintf("[Clash]") + } + return fmt.Sprintf("%s", m.SourceAddress()) } } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 5e2fa1fdc..6d06470f2 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -74,15 +74,17 @@ func ApplyConfig(cfg *config.Config, force bool) { defer mux.Unlock() updateUsers(cfg.Users) - updateProxies(cfg.Proxies, cfg.Providers) - updateRules(cfg.Rules, cfg.RuleProviders) updateHosts(cfg.Hosts) updateProfile(cfg) + updateProxies(cfg.Proxies, cfg.Providers) + updateRules(cfg.Rules, cfg.RuleProviders) updateIPTables(cfg.DNS, cfg.General, cfg.Tun) updateDNS(cfg.DNS, cfg.Tun) updateGeneral(cfg.General, cfg.Tun, force) updateTun(cfg.Tun) updateExperimental(cfg) + + loadProvider(cfg.RuleProviders, cfg.Providers) } func GetGeneral() *config.General { @@ -175,6 +177,38 @@ func updateRules(rules []C.Rule, ruleProviders map[string]*provider.RuleProvider tunnel.UpdateRules(rules, ruleProviders) } +func loadProvider(ruleProviders map[string]*provider.RuleProvider, proxyProviders map[string]provider.ProxyProvider) { + load := func(pv provider.Provider) { + if pv.VehicleType() == provider.Compatible { + log.Infoln("Start initial compatible provider %s", pv.Name()) + } else { + log.Infoln("Start initial provider %s", (pv).Name()) + } + + if err := (pv).Initial(); err != nil { + switch pv.Type() { + case provider.Proxy: + { + log.Warnln("initial proxy provider %s error: %v", (pv).Name(), err) + } + case provider.Rule: + { + log.Warnln("initial rule provider %s error: %v", (pv).Name(), err) + } + + } + } + } + + for _, proxyProvider := range proxyProviders { + load(proxyProvider) + } + + for _, ruleProvider := range ruleProviders { + load(*ruleProvider) + } +} + func updateGeneral(general *config.General, Tun *config.Tun, force bool) { tunnel.SetMode(general.Mode) resolver.DisableIPv6 = !general.IPv6 diff --git a/listener/inner/tcp.go b/listener/inner/tcp.go new file mode 100644 index 000000000..a7e385889 --- /dev/null +++ b/listener/inner/tcp.go @@ -0,0 +1,20 @@ +package inner + +import ( + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "net" +) + +var tcpIn chan<- C.ConnContext + +func New(in chan<- C.ConnContext) { + tcpIn = in +} + +func HandleTcp(dst string, host string) net.Conn { + conn1, conn2 := net.Pipe() + context := inbound.NewInner(conn2, dst, host) + tcpIn <- context + return conn1 +} diff --git a/listener/listener.go b/listener/listener.go index 11e02e27a..675abb5d9 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -2,6 +2,7 @@ package proxy import ( "fmt" + "github.com/Dreamacro/clash/listener/inner" "net" "runtime" "strconv" @@ -124,6 +125,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Errorln("Start SOCKS server error: %s", err.Error()) } }() + inner.New(tcpIn) addr := genAddr(bindAddress, port, allowLan) diff --git a/rule/provider.go b/rule/provider.go index ea409f551..561dd12a0 100644 --- a/rule/provider.go +++ b/rule/provider.go @@ -75,6 +75,10 @@ func (rp *ruleSetProvider) Behavior() P.RuleType { } func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool { + if rp.count == 0 { + return false + } + switch rp.behavior { case P.Domain: return rp.DomainRules.Search(metadata.Host) != nil