[Refactor]

1.allow maybe empty group
2.use COMPATIBLE(DIRECT alias) when proxy group is empty
3.http provider pass through tunnel
This commit is contained in:
Skyxim 2022-01-18 21:09:36 +08:00
parent ee6c1871a9
commit b15344ec78
17 changed files with 135 additions and 50 deletions

View File

@ -20,3 +20,16 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo
return context.NewConnContext(conn, metadata) 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)
}

View File

@ -44,3 +44,13 @@ func NewDirect() *Direct {
}, },
} }
} }
func NewCompatible() *Direct {
return &Direct{
Base: &Base{
name: "COMPATIBLE",
tp: C.Compatible,
udp: true,
},
}
}

View File

@ -1,6 +1,7 @@
package outboundgroup package outboundgroup
import ( import (
"github.com/Dreamacro/clash/tunnel"
"regexp" "regexp"
"time" "time"
@ -21,6 +22,7 @@ func getProvidersProxies(providers []provider.ProxyProvider, touch bool, filter
proxies = append(proxies, provider.Proxies()...) proxies = append(proxies, provider.Proxies()...)
} }
} }
var filterReg *regexp.Regexp = nil var filterReg *regexp.Regexp = nil
matchedProxies := []C.Proxy{} matchedProxies := []C.Proxy{}
if len(filter) > 0 { 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 no proxy matched, means bad filter, return all proxies
if len(matchedProxies) > 0 { 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
} }

View File

@ -72,7 +72,7 @@ func (f *Fallback) onDialFailed() {
} else { } else {
failedCount := f.failedTimes.Inc() failedCount := f.failedTimes.Inc()
log.Warnln("%s failed count: %d", f.Name(), failedCount) 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()) log.Warnln("because %s failed multiple times, active health check", f.Name())
for _, proxyProvider := range f.providers { for _, proxyProvider := range f.providers {
go proxyProvider.HealthCheck() go proxyProvider.HealthCheck()
@ -97,10 +97,11 @@ func (f *Fallback) SupportUDP() bool {
// MarshalJSON implements C.ProxyAdapter // MarshalJSON implements C.ProxyAdapter
func (f *Fallback) MarshalJSON() ([]byte, error) { func (f *Fallback) MarshalJSON() ([]byte, error) {
var all []string all := make([]string, 0)
for _, proxy := range f.proxies(false) { for _, proxy := range f.proxies(false) {
all = append(all, proxy.Name()) all = append(all, proxy.Name())
} }
return json.Marshal(map[string]interface{}{ return json.Marshal(map[string]interface{}{
"type": f.Type().String(), "type": f.Type().String(),
"now": f.Now(), "now": f.Now(),

View File

@ -150,10 +150,12 @@ func (lb *LoadBalance) proxies(touch bool) []C.Proxy {
// MarshalJSON implements C.ProxyAdapter // MarshalJSON implements C.ProxyAdapter
func (lb *LoadBalance) MarshalJSON() ([]byte, error) { func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
var all []string all := make([]string, 0)
for _, proxy := range lb.proxies(false) { for _, proxy := range lb.proxies(false) {
all = append(all, proxy.Name()) all = append(all, proxy.Name())
} }
return json.Marshal(map[string]interface{}{ return json.Marshal(map[string]interface{}{
"type": lb.Type().String(), "type": lb.Type().String(),
"all": all, "all": all,

View File

@ -23,7 +23,7 @@ type Relay struct {
func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
var proxies []C.Proxy var proxies []C.Proxy
for _, proxy := range r.proxies(metadata, true) { 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) proxies = append(proxies, proxy)
} }
} }
@ -69,10 +69,12 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
// MarshalJSON implements C.ProxyAdapter // MarshalJSON implements C.ProxyAdapter
func (r *Relay) MarshalJSON() ([]byte, error) { func (r *Relay) MarshalJSON() ([]byte, error) {
var all []string all := make([]string, 0)
for _, proxy := range r.rawProxies(false) { for _, proxy := range r.rawProxies(false) {
all = append(all, proxy.Name()) all = append(all, proxy.Name())
} }
return json.Marshal(map[string]interface{}{ return json.Marshal(map[string]interface{}{
"type": r.Type().String(), "type": r.Type().String(),
"all": all, "all": all,

View File

@ -50,7 +50,8 @@ func (s *Selector) SupportUDP() bool {
// MarshalJSON implements C.ProxyAdapter // MarshalJSON implements C.ProxyAdapter
func (s *Selector) MarshalJSON() ([]byte, error) { func (s *Selector) MarshalJSON() ([]byte, error) {
var all []string all := make([]string, 0)
for _, proxy := range getProvidersProxies(s.providers, false, s.filter) { for _, proxy := range getProvidersProxies(s.providers, false, s.filter) {
all = append(all, proxy.Name()) all = append(all, proxy.Name())
} }

View File

@ -125,10 +125,12 @@ func (u *URLTest) SupportUDP() bool {
// MarshalJSON implements C.ProxyAdapter // MarshalJSON implements C.ProxyAdapter
func (u *URLTest) MarshalJSON() ([]byte, error) { func (u *URLTest) MarshalJSON() ([]byte, error) {
var all []string all := make([]string, 0)
for _, proxy := range u.proxies(false) { for _, proxy := range u.proxies(false) {
all = append(all, proxy.Name()) all = append(all, proxy.Name())
} }
return json.Marshal(map[string]interface{}{ return json.Marshal(map[string]interface{}{
"type": u.Type().String(), "type": u.Type().String(),
"now": u.Now(), "now": u.Now(),
@ -149,7 +151,7 @@ func (u *URLTest) onDialFailed() {
} else { } else {
failedCount := u.failedTimes.Inc() failedCount := u.failedTimes.Inc()
log.Warnln("%s failed count: %d", u.Name(), failedCount) 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()) log.Warnln("because %s failed multiple times, active health check", u.Name())
for _, proxyProvider := range u.providers { for _, proxyProvider := range u.providers {
go proxyProvider.HealthCheck() go proxyProvider.HealthCheck()

View File

@ -67,6 +67,10 @@ func (pp *proxySetProvider) Initial() error {
} }
pp.onUpdate(elm) pp.onUpdate(elm)
if pp.healthCheck.auto() {
go pp.healthCheck.process()
}
return nil 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) return nil, fmt.Errorf("invalid filter regex: %w", err)
} }
if hc.auto() {
go hc.process()
}
pd := &proxySetProvider{ pd := &proxySetProvider{
proxies: []C.Proxy{}, proxies: []C.Proxy{},
healthCheck: hc, healthCheck: hc,
@ -190,6 +190,10 @@ func (cp *compatibleProvider) Update() error {
} }
func (cp *compatibleProvider) Initial() error { func (cp *compatibleProvider) Initial() error {
if cp.healthCheck.auto() {
go cp.healthCheck.process()
}
return nil 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") return nil, errors.New("provider need one proxy at least")
} }
if hc.auto() {
go hc.process()
}
pd := &compatibleProvider{ pd := &compatibleProvider{
name: name, name: name,
proxies: proxies, proxies: proxies,

View File

@ -2,6 +2,7 @@ package provider
import ( import (
"context" "context"
"github.com/Dreamacro/clash/listener/inner"
"io" "io"
"net" "net"
"net/http" "net/http"
@ -10,7 +11,6 @@ import (
"time" "time"
netHttp "github.com/Dreamacro/clash/common/net" netHttp "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
types "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider"
) )
@ -77,7 +77,8 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { 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
}, },
} }

View File

@ -350,6 +350,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect()) proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
proxies["REJECT"] = adapter.NewProxy(outbound.NewReject()) proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible())
proxyList = append(proxyList, "DIRECT", "REJECT") proxyList = append(proxyList, "DIRECT", "REJECT")
// parse proxy // parse proxy
@ -396,13 +397,6 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
providersMap[name] = pd 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 // parse proxy group
for idx, mapping := range groupsConfig { for idx, mapping := range groupsConfig {
group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap) 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) 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 var ps []C.Proxy
for _, v := range proxyList { for _, v := range proxyList {
ps = append(ps, proxies[v]) 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) 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 var rules []C.Rule
rulesConfig := cfg.Rule rulesConfig := cfg.Rule
mode := cfg.Mode mode := cfg.Mode

View File

@ -13,7 +13,7 @@ import (
const ( const (
Direct AdapterType = iota Direct AdapterType = iota
Reject Reject
Compatible
Shadowsocks Shadowsocks
ShadowsocksR ShadowsocksR
Snell Snell
@ -128,7 +128,8 @@ func (at AdapterType) String() string {
return "Direct" return "Direct"
case Reject: case Reject:
return "Reject" return "Reject"
case Compatible:
return "Compatible"
case Shadowsocks: case Shadowsocks:
return "Shadowsocks" return "Shadowsocks"
case ShadowsocksR: case ShadowsocksR:

View File

@ -24,6 +24,7 @@ const (
REDIR REDIR
TPROXY TPROXY
TUN TUN
INNER
) )
type NetWork int type NetWork int
@ -59,6 +60,8 @@ func (t Type) String() string {
return "TProxy" return "TProxy"
case TUN: case TUN:
return "Tun" return "Tun"
case INNER:
return "Inner"
default: default:
return "Unknown" return "Unknown"
} }
@ -94,6 +97,10 @@ func (m *Metadata) SourceDetail() string {
if m.Process != "" { if m.Process != "" {
return fmt.Sprintf("%s(%s)", m.SourceAddress(), m.Process) return fmt.Sprintf("%s(%s)", m.SourceAddress(), m.Process)
} else { } else {
if m.Type == INNER {
return fmt.Sprintf("[Clash]")
}
return fmt.Sprintf("%s", m.SourceAddress()) return fmt.Sprintf("%s", m.SourceAddress())
} }
} }

View File

@ -74,15 +74,17 @@ func ApplyConfig(cfg *config.Config, force bool) {
defer mux.Unlock() defer mux.Unlock()
updateUsers(cfg.Users) updateUsers(cfg.Users)
updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.RuleProviders)
updateHosts(cfg.Hosts) updateHosts(cfg.Hosts)
updateProfile(cfg) updateProfile(cfg)
updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.RuleProviders)
updateIPTables(cfg.DNS, cfg.General, cfg.Tun) updateIPTables(cfg.DNS, cfg.General, cfg.Tun)
updateDNS(cfg.DNS, cfg.Tun) updateDNS(cfg.DNS, cfg.Tun)
updateGeneral(cfg.General, cfg.Tun, force) updateGeneral(cfg.General, cfg.Tun, force)
updateTun(cfg.Tun) updateTun(cfg.Tun)
updateExperimental(cfg) updateExperimental(cfg)
loadProvider(cfg.RuleProviders, cfg.Providers)
} }
func GetGeneral() *config.General { func GetGeneral() *config.General {
@ -175,6 +177,38 @@ func updateRules(rules []C.Rule, ruleProviders map[string]*provider.RuleProvider
tunnel.UpdateRules(rules, ruleProviders) 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) { func updateGeneral(general *config.General, Tun *config.Tun, force bool) {
tunnel.SetMode(general.Mode) tunnel.SetMode(general.Mode)
resolver.DisableIPv6 = !general.IPv6 resolver.DisableIPv6 = !general.IPv6

20
listener/inner/tcp.go Normal file
View File

@ -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
}

View File

@ -2,6 +2,7 @@ package proxy
import ( import (
"fmt" "fmt"
"github.com/Dreamacro/clash/listener/inner"
"net" "net"
"runtime" "runtime"
"strconv" "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()) log.Errorln("Start SOCKS server error: %s", err.Error())
} }
}() }()
inner.New(tcpIn)
addr := genAddr(bindAddress, port, allowLan) addr := genAddr(bindAddress, port, allowLan)

View File

@ -75,6 +75,10 @@ func (rp *ruleSetProvider) Behavior() P.RuleType {
} }
func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool { func (rp *ruleSetProvider) Match(metadata *C.Metadata) bool {
if rp.count == 0 {
return false
}
switch rp.behavior { switch rp.behavior {
case P.Domain: case P.Domain:
return rp.DomainRules.Search(metadata.Host) != nil return rp.DomainRules.Search(metadata.Host) != nil