[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)
}
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
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
}

View file

@ -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(),

View file

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

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) {
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,

View file

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

View file

@ -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()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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 (
"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)

View file

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