mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 12:42:27 +08:00
feat: support tcp concurrent, Separate dialing and dns resolver ipv6
tcp-concurrent:true
This commit is contained in:
parent
2e1d9a4f2e
commit
81b5543b0d
@ -3,12 +3,15 @@ package dialer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DisableIPv6 = false
|
||||||
|
|
||||||
func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) {
|
func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) {
|
||||||
opt := &option{
|
opt := &option{
|
||||||
interfaceName: DefaultInterface.Load(),
|
interfaceName: DefaultInterface.Load(),
|
||||||
@ -51,7 +54,11 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
|||||||
|
|
||||||
return dialContext(ctx, network, ip, port, opt)
|
return dialContext(ctx, network, ip, port, opt)
|
||||||
case "tcp", "udp":
|
case "tcp", "udp":
|
||||||
return dualStackDialContext(ctx, network, address, opt)
|
if TCPConcurrent && network == "tcp" {
|
||||||
|
return concurrentDialContext(ctx, network, address, opt)
|
||||||
|
} else {
|
||||||
|
return dualStackDialContext(ctx, network, address, opt)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("network invalid")
|
return nil, errors.New("network invalid")
|
||||||
}
|
}
|
||||||
@ -183,3 +190,73 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
|||||||
|
|
||||||
return nil, errors.New("never touched")
|
return nil, errors.New("never touched")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func concurrentDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
returned := make(chan struct{})
|
||||||
|
defer close(returned)
|
||||||
|
|
||||||
|
type dialResult struct {
|
||||||
|
ip netip.Addr
|
||||||
|
net.Conn
|
||||||
|
error
|
||||||
|
resolved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make(chan dialResult)
|
||||||
|
var ips []netip.Addr
|
||||||
|
|
||||||
|
if opt.direct {
|
||||||
|
ips, err = resolver.ResolveAllIP(host)
|
||||||
|
} else {
|
||||||
|
ips, err = resolver.ResolveAllIPProxyServerHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpRacer := func(ctx context.Context, ip netip.Addr) {
|
||||||
|
result := dialResult{ip: ip}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
select {
|
||||||
|
case results <- result:
|
||||||
|
case <-returned:
|
||||||
|
if result.Conn != nil {
|
||||||
|
result.Conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
v := "4"
|
||||||
|
if ip.Is6() {
|
||||||
|
v = "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugln("[%s] try use [%s] connected", host, ip.String())
|
||||||
|
result.Conn, result.error = dialContext(ctx, network+v, ip, port, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
go tcpRacer(ctx, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
connCount := len(ips)
|
||||||
|
for res := range results {
|
||||||
|
connCount--
|
||||||
|
if res.error == nil {
|
||||||
|
connIp := res.Conn.RemoteAddr()
|
||||||
|
log.Debugln("[%s] used [%s] connected", host, connIp)
|
||||||
|
return res.Conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Errorln("connect error:%v", res.error)
|
||||||
|
if connCount == 0 {
|
||||||
|
log.Errorln("connect [%s] all ip failed", host)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("all ip tcp shakeHands failed")
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ var (
|
|||||||
DefaultOptions []Option
|
DefaultOptions []Option
|
||||||
DefaultInterface = atomic.NewString("")
|
DefaultInterface = atomic.NewString("")
|
||||||
DefaultRoutingMark = atomic.NewInt32(0)
|
DefaultRoutingMark = atomic.NewInt32(0)
|
||||||
|
TCPConcurrent = false
|
||||||
)
|
)
|
||||||
|
|
||||||
type option struct {
|
type option struct {
|
||||||
|
@ -40,6 +40,10 @@ type Resolver interface {
|
|||||||
ResolveIP(host string) (ip netip.Addr, err error)
|
ResolveIP(host string) (ip netip.Addr, err error)
|
||||||
ResolveIPv4(host string) (ip netip.Addr, err error)
|
ResolveIPv4(host string) (ip netip.Addr, err error)
|
||||||
ResolveIPv6(host string) (ip netip.Addr, err error)
|
ResolveIPv6(host string) (ip netip.Addr, err error)
|
||||||
|
ResolveAllIP(host string) (ip []netip.Addr, err error)
|
||||||
|
ResolveAllIPPrimaryIPv4(host string) (ips []netip.Addr, err error)
|
||||||
|
ResolveAllIPv4(host string) (ips []netip.Addr, err error)
|
||||||
|
ResolveAllIPv6(host string) (ips []netip.Addr, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 with a host, return ipv4
|
// ResolveIPv4 with a host, return ipv4
|
||||||
@ -191,3 +195,51 @@ func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
|||||||
}
|
}
|
||||||
return ResolveIP(host)
|
return ResolveIP(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||||
|
return r.ResolveAllIPv6(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||||
|
return r.ResolveAllIPv4(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||||
|
return r.ResolveAllIP(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIP(host string) ([]netip.Addr, error) {
|
||||||
|
return ResolveAllIPWithResolver(host, DefaultResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv4(host string) ([]netip.Addr, error) {
|
||||||
|
return ResolveAllIPv4WithResolver(host, DefaultResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv6(host string) ([]netip.Addr, error) {
|
||||||
|
return ResolveAllIPv6WithResolver(host, DefaultResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv6ProxyServerHost(host string) ([]netip.Addr, error) {
|
||||||
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveAllIPv6WithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveAllIPv6(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPv4ProxyServerHost(host string) ([]netip.Addr, error) {
|
||||||
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveAllIPv4WithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveAllIPv4(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllIPProxyServerHost(host string) ([]netip.Addr, error) {
|
||||||
|
if ProxyServerHostResolver != nil {
|
||||||
|
return ResolveAllIPWithResolver(host, ProxyServerHostResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveAllIP(host)
|
||||||
|
}
|
||||||
|
@ -49,6 +49,7 @@ type General struct {
|
|||||||
RoutingMark int `json:"-"`
|
RoutingMark int `json:"-"`
|
||||||
GeodataMode bool `json:"geodata-mode"`
|
GeodataMode bool `json:"geodata-mode"`
|
||||||
GeodataLoader string `json:"geodata-loader"`
|
GeodataLoader string `json:"geodata-loader"`
|
||||||
|
TCPConcurrent bool `json:"tcp-concurrent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inbound config
|
// Inbound config
|
||||||
@ -206,6 +207,7 @@ type RawConfig struct {
|
|||||||
RoutingMark int `yaml:"routing-mark"`
|
RoutingMark int `yaml:"routing-mark"`
|
||||||
GeodataMode bool `yaml:"geodata-mode"`
|
GeodataMode bool `yaml:"geodata-mode"`
|
||||||
GeodataLoader string `yaml:"geodata-loader"`
|
GeodataLoader string `yaml:"geodata-loader"`
|
||||||
|
TCPConcurrent bool `yaml:"tc-pconcurrent" json:"tc-pconcurrent"`
|
||||||
|
|
||||||
Sniffer SnifferRaw `yaml:"sniffer"`
|
Sniffer SnifferRaw `yaml:"sniffer"`
|
||||||
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
||||||
@ -256,6 +258,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
Rule: []string{},
|
Rule: []string{},
|
||||||
Proxy: []map[string]any{},
|
Proxy: []map[string]any{},
|
||||||
ProxyGroup: []map[string]any{},
|
ProxyGroup: []map[string]any{},
|
||||||
|
TCPConcurrent: false,
|
||||||
Tun: RawTun{
|
Tun: RawTun{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Device: "",
|
Device: "",
|
||||||
@ -412,6 +415,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
RoutingMark: cfg.RoutingMark,
|
RoutingMark: cfg.RoutingMark,
|
||||||
GeodataMode: cfg.GeodataMode,
|
GeodataMode: cfg.GeodataMode,
|
||||||
GeodataLoader: cfg.GeodataLoader,
|
GeodataLoader: cfg.GeodataLoader,
|
||||||
|
TCPConcurrent: cfg.TCPConcurrent,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +44,8 @@ type Resolver struct {
|
|||||||
proxyServer []dnsClient
|
proxyServer []dnsClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
func (r *Resolver) ResolveAllIPPrimaryIPv4(host string) (ips []netip.Addr, err error) {
|
||||||
func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
|
ch := make(chan []netip.Addr, 1)
|
||||||
ch := make(chan netip.Addr, 1)
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
ip, err := r.resolveIP(host, D.TypeAAAA)
|
ip, err := r.resolveIP(host, D.TypeAAAA)
|
||||||
@ -56,27 +55,75 @@ func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
|
|||||||
ch <- ip
|
ch <- ip
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ip, err = r.resolveIP(host, D.TypeA)
|
ips, err = r.resolveIP(host, D.TypeA)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, open := <-ch
|
ip, open := <-ch
|
||||||
if !open {
|
if !open {
|
||||||
return netip.Addr{}, resolver.ErrIPNotFound
|
return nil, resolver.ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) {
|
||||||
|
ch := make(chan []netip.Addr, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
ip, err := r.resolveIP(host, D.TypeAAAA)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- ip
|
||||||
|
}()
|
||||||
|
|
||||||
|
ips, err = r.resolveIP(host, D.TypeA)
|
||||||
|
|
||||||
|
ipv6s, open := <-ch
|
||||||
|
if !open && err != nil {
|
||||||
|
return nil, resolver.ErrIPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
ips = append(ips, ipv6s...)
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) ResolveAllIPv4(host string) (ips []netip.Addr, err error) {
|
||||||
|
return r.resolveIP(host, D.TypeA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) ResolveAllIPv6(host string) (ips []netip.Addr, err error) {
|
||||||
|
return r.resolveIP(host, D.TypeAAAA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
||||||
|
func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) {
|
||||||
|
if ips, err := r.ResolveAllIPPrimaryIPv4(host); err == nil {
|
||||||
|
return ips[rand.Intn(len(ips))], nil
|
||||||
|
} else {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ResolveIPv4 request with TypeA
|
// ResolveIPv4 request with TypeA
|
||||||
func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) {
|
func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeA)
|
if ips, err := r.ResolveAllIPv4(host); err == nil {
|
||||||
|
return ips[rand.Intn(len(ips))], nil
|
||||||
|
} else {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6 request with TypeAAAA
|
// ResolveIPv6 request with TypeAAAA
|
||||||
func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) {
|
func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) {
|
||||||
return r.resolveIP(host, D.TypeAAAA)
|
if ips, err := r.ResolveAllIPv6(host); err == nil {
|
||||||
|
return ips[rand.Intn(len(ips))], nil
|
||||||
|
} else {
|
||||||
|
return netip.Addr{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
||||||
@ -254,16 +301,16 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err error) {
|
func (r *Resolver) resolveIP(host string, dnsType uint16) (ips []netip.Addr, err error) {
|
||||||
ip, err = netip.ParseAddr(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isIPv4 := ip.Is4()
|
isIPv4 := ip.Is4()
|
||||||
if dnsType == D.TypeAAAA && !isIPv4 {
|
if dnsType == D.TypeAAAA && !isIPv4 {
|
||||||
return ip, nil
|
return []netip.Addr{ip}, nil
|
||||||
} else if dnsType == D.TypeA && isIPv4 {
|
} else if dnsType == D.TypeA && isIPv4 {
|
||||||
return ip, nil
|
return []netip.Addr{ip}, nil
|
||||||
} else {
|
} else {
|
||||||
return netip.Addr{}, resolver.ErrIPVersion
|
return []netip.Addr{}, resolver.ErrIPVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,16 +319,15 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ip netip.Addr, err er
|
|||||||
|
|
||||||
msg, err := r.Exchange(query)
|
msg, err := r.Exchange(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return netip.Addr{}, err
|
return []netip.Addr{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ips := msgToIP(msg)
|
ips = msgToIP(msg)
|
||||||
ipLength := len(ips)
|
ipLength := len(ips)
|
||||||
if ipLength == 0 {
|
if ipLength == 0 {
|
||||||
return netip.Addr{}, resolver.ErrIPNotFound
|
return []netip.Addr{}, resolver.ErrIPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ips[rand.Intn(ipLength)]
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +139,8 @@ func updateDNS(c *config.DNS, t *config.Tun) {
|
|||||||
ProxyServer: c.ProxyServerNameserver,
|
ProxyServer: c.ProxyServerNameserver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolver.DisableIPv6 = cfg.IPv6
|
||||||
|
|
||||||
r := dns.NewResolver(cfg)
|
r := dns.NewResolver(cfg)
|
||||||
pr := dns.NewProxyServerHostResolver(r)
|
pr := dns.NewProxyServerHostResolver(r)
|
||||||
m := dns.NewEnhancer(cfg)
|
m := dns.NewEnhancer(cfg)
|
||||||
@ -157,6 +159,7 @@ func updateDNS(c *config.DNS, t *config.Tun) {
|
|||||||
|
|
||||||
if t.Enable {
|
if t.Enable {
|
||||||
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
||||||
|
log.Infoln("DNS enable IPv6 resolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Enable {
|
if c.Enable {
|
||||||
@ -243,10 +246,19 @@ func updateSniffer(sniffer *config.Sniffer) {
|
|||||||
func updateGeneral(general *config.General, force bool) {
|
func updateGeneral(general *config.General, force bool) {
|
||||||
log.SetLevel(general.LogLevel)
|
log.SetLevel(general.LogLevel)
|
||||||
tunnel.SetMode(general.Mode)
|
tunnel.SetMode(general.Mode)
|
||||||
resolver.DisableIPv6 = !general.IPv6
|
dialer.DisableIPv6 = general.IPv6
|
||||||
|
if !dialer.DisableIPv6 {
|
||||||
|
resolver.DisableIPv6 = dialer.DisableIPv6
|
||||||
|
} else {
|
||||||
|
log.Infoln("Use IPv6")
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.TCPConcurrent = general.TCPConcurrent
|
||||||
|
if dialer.TCPConcurrent {
|
||||||
|
log.Infoln("Use tcp concurrent")
|
||||||
|
}
|
||||||
|
|
||||||
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
||||||
|
|
||||||
dialer.DefaultInterface.Store(general.Interface)
|
dialer.DefaultInterface.Store(general.Interface)
|
||||||
|
|
||||||
if dialer.DefaultInterface.Load() != "" {
|
if dialer.DefaultInterface.Load() != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user