mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 06:33:14 +08:00
feat: add ip-version param
This commit is contained in:
parent
42e489e199
commit
99effb051b
@ -13,13 +13,14 @@ import (
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
name string
|
||||
addr string
|
||||
iface string
|
||||
tp C.AdapterType
|
||||
udp bool
|
||||
rmark int
|
||||
id string
|
||||
name string
|
||||
addr string
|
||||
iface string
|
||||
tp C.AdapterType
|
||||
udp bool
|
||||
rmark int
|
||||
id string
|
||||
prefer C.DNSPrefer
|
||||
}
|
||||
|
||||
// Name implements C.ProxyAdapter
|
||||
@ -103,12 +104,25 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
|
||||
opts = append(opts, dialer.WithRoutingMark(b.rmark))
|
||||
}
|
||||
|
||||
switch b.prefer {
|
||||
case C.IPv4Only:
|
||||
opts = append(opts, dialer.WithOnlySingleStack(true))
|
||||
case C.IPv6Only:
|
||||
opts = append(opts, dialer.WithOnlySingleStack(false))
|
||||
case C.IPv4Prefer:
|
||||
opts = append(opts, dialer.WithPreferIPv4())
|
||||
case C.IPv6Prefer:
|
||||
opts = append(opts, dialer.WithPreferIPv6())
|
||||
default:
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
type BasicOption struct {
|
||||
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
||||
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
||||
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
|
||||
}
|
||||
|
||||
type BaseOption struct {
|
||||
@ -118,16 +132,18 @@ type BaseOption struct {
|
||||
UDP bool
|
||||
Interface string
|
||||
RoutingMark int
|
||||
Prefer C.DNSPrefer
|
||||
}
|
||||
|
||||
func NewBase(opt BaseOption) *Base {
|
||||
return &Base{
|
||||
name: opt.Name,
|
||||
addr: opt.Addr,
|
||||
tp: opt.Type,
|
||||
udp: opt.UDP,
|
||||
iface: opt.Interface,
|
||||
rmark: opt.RoutingMark,
|
||||
name: opt.Name,
|
||||
addr: opt.Addr,
|
||||
tp: opt.Type,
|
||||
udp: opt.UDP,
|
||||
iface: opt.Interface,
|
||||
rmark: opt.RoutingMark,
|
||||
prefer: opt.Prefer,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,10 @@ type directPacketConn struct {
|
||||
func NewDirect() *Direct {
|
||||
return &Direct{
|
||||
Base: &Base{
|
||||
name: "DIRECT",
|
||||
tp: C.Direct,
|
||||
udp: true,
|
||||
name: "DIRECT",
|
||||
tp: C.Direct,
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -50,9 +51,10 @@ func NewDirect() *Direct {
|
||||
func NewCompatible() *Direct {
|
||||
return &Direct{
|
||||
Base: &Base{
|
||||
name: "COMPATIBLE",
|
||||
tp: C.Compatible,
|
||||
udp: true,
|
||||
name: "COMPATIBLE",
|
||||
tp: C.Compatible,
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -153,11 +153,12 @@ func NewHttp(option HttpOption) (*Http, error) {
|
||||
|
||||
return &Http{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Http,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Http,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
user: option.UserName,
|
||||
pass: option.Password,
|
||||
|
@ -53,6 +53,9 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
hyDialer: func() (net.PacketConn, error) {
|
||||
return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...)
|
||||
},
|
||||
remoteAddr: func(addr string) (net.Addr, error) {
|
||||
return resolveUDPAddrWithPrefer("udp", addr, h.prefer)
|
||||
},
|
||||
}
|
||||
|
||||
tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc)
|
||||
@ -184,11 +187,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||
option.Protocol = DefaultProtocol
|
||||
}
|
||||
if option.ReceiveWindowConn == 0 {
|
||||
quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow
|
||||
quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10
|
||||
quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow
|
||||
}
|
||||
if option.ReceiveWindow == 0 {
|
||||
quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow
|
||||
quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10
|
||||
quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow
|
||||
}
|
||||
if !quicConfig.DisablePathMTUDiscovery && pmtud_fix.DisablePathMTUDiscovery {
|
||||
@ -216,12 +219,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||
}
|
||||
return &Hysteria{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Hysteria,
|
||||
udp: true,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Hysteria,
|
||||
udp: true,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
client: client,
|
||||
}, nil
|
||||
@ -287,8 +291,9 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}
|
||||
|
||||
type hyDialerWithContext struct {
|
||||
hyDialer func() (net.PacketConn, error)
|
||||
ctx context.Context
|
||||
hyDialer func() (net.PacketConn, error)
|
||||
ctx context.Context
|
||||
remoteAddr func(host string) (net.Addr, error)
|
||||
}
|
||||
|
||||
func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) {
|
||||
@ -298,3 +303,7 @@ func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) {
|
||||
func (h *hyDialerWithContext) Context() context.Context {
|
||||
return h.ctx
|
||||
}
|
||||
|
||||
func (h *hyDialerWithContext) RemoteAddr(host string) (net.Addr, error) {
|
||||
return h.remoteAddr(host)
|
||||
}
|
||||
|
@ -27,9 +27,10 @@ func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
||||
func NewReject() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
name: "REJECT",
|
||||
tp: C.Reject,
|
||||
udp: true,
|
||||
name: "REJECT",
|
||||
tp: C.Reject,
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -37,9 +38,10 @@ func NewReject() *Reject {
|
||||
func NewPass() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
name: "PASS",
|
||||
tp: C.Pass,
|
||||
udp: true,
|
||||
name: "PASS",
|
||||
tp: C.Pass,
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := resolveUDPAddr("udp", ss.addr)
|
||||
addr, err := resolveUDPAddrWithPrefer("udp", ss.addr, ss.prefer)
|
||||
if err != nil {
|
||||
pc.Close()
|
||||
return nil, err
|
||||
@ -186,12 +186,13 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
||||
|
||||
return &ShadowSocks{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Shadowsocks,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Shadowsocks,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
method: method,
|
||||
|
||||
|
@ -79,7 +79,7 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := resolveUDPAddr("udp", ssr.addr)
|
||||
addr, err := resolveUDPAddrWithPrefer("udp", ssr.addr, ssr.prefer)
|
||||
if err != nil {
|
||||
pc.Close()
|
||||
return nil, err
|
||||
@ -143,12 +143,13 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
||||
|
||||
return &ShadowSocksR{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.ShadowsocksR,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.ShadowsocksR,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
cipher: coreCiph,
|
||||
obfs: obfs,
|
||||
|
@ -152,12 +152,13 @@ func NewSnell(option SnellOption) (*Snell, error) {
|
||||
|
||||
s := &Snell{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Snell,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Snell,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
psk: psk,
|
||||
obfsOption: obfsOption,
|
||||
|
@ -160,12 +160,13 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
|
||||
|
||||
return &Socks5{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Socks5,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Socks5,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
user: option.UserName,
|
||||
pass: option.Password,
|
||||
|
@ -209,12 +209,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||
|
||||
t := &Trojan{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Trojan,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Trojan,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
instance: trojan.New(tOption),
|
||||
option: &option,
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
xtls "github.com/xtls/go"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@ -74,6 +75,59 @@ func resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
||||
return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port))
|
||||
}
|
||||
|
||||
func resolveUDPAddrWithPrefer(network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ip netip.Addr
|
||||
switch prefer {
|
||||
case C.IPv4Only:
|
||||
ip, err = resolver.ResolveIPv4ProxyServerHost(host)
|
||||
case C.IPv6Only:
|
||||
ip, err = resolver.ResolveIPv6ProxyServerHost(host)
|
||||
case C.IPv6Prefer:
|
||||
var ips []netip.Addr
|
||||
ips, err = resolver.ResolveAllIPProxyServerHost(host)
|
||||
var fallback netip.Addr
|
||||
if err == nil {
|
||||
for _, addr := range ips {
|
||||
if addr.Is6() {
|
||||
ip = addr
|
||||
break
|
||||
} else {
|
||||
if !fallback.IsValid() {
|
||||
fallback = addr
|
||||
}
|
||||
}
|
||||
}
|
||||
ip = fallback
|
||||
}
|
||||
case C.IPv4Prefer | C.DualStack:
|
||||
var ips []netip.Addr
|
||||
ips, err = resolver.ResolveAllIPProxyServerHost(host)
|
||||
var fallback netip.Addr
|
||||
if err == nil {
|
||||
for _, addr := range ips {
|
||||
if addr.Is4() {
|
||||
ip = addr
|
||||
break
|
||||
} else {
|
||||
if !fallback.IsValid() {
|
||||
fallback = addr
|
||||
}
|
||||
}
|
||||
}
|
||||
ip = fallback
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port))
|
||||
}
|
||||
|
||||
func safeConnClose(c net.Conn, err error) {
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
|
@ -418,11 +418,12 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||
|
||||
v := &Vless{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Vless,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Vless,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
client: client,
|
||||
option: &option,
|
||||
|
@ -316,12 +316,13 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
|
||||
v := &Vmess{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Vmess,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
name: option.Name,
|
||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||
tp: C.Vmess,
|
||||
udp: option.UDP,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
client: client,
|
||||
option: &option,
|
||||
|
@ -5,8 +5,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"go.uber.org/atomic"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@ -16,6 +18,8 @@ var (
|
||||
actualDualStackDialContext = dualStackDialContext
|
||||
tcpConcurrent = false
|
||||
DisableIPv6 = false
|
||||
ErrorInvalidedNetworkStack = errors.New("invalided network stack")
|
||||
ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel")
|
||||
)
|
||||
|
||||
func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) {
|
||||
@ -32,13 +36,23 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
||||
o(opt)
|
||||
}
|
||||
|
||||
if opt.network == 4 || opt.network == 6 {
|
||||
if strings.Contains(network, "tcp") {
|
||||
network = "tcp"
|
||||
} else {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
network = fmt.Sprintf("%s%d", network, opt.network)
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp4", "tcp6", "udp4", "udp6":
|
||||
return actualSingleDialContext(ctx, network, address, opt)
|
||||
case "tcp", "udp":
|
||||
return actualDualStackDialContext(ctx, network, address, opt)
|
||||
default:
|
||||
return nil, errors.New("network invalid")
|
||||
return nil, ErrorInvalidedNetworkStack
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,10 +70,6 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
||||
o(cfg)
|
||||
}
|
||||
|
||||
if DisableIPv6 {
|
||||
network = "udp4"
|
||||
}
|
||||
|
||||
lc := &net.ListenConfig{}
|
||||
if cfg.interfaceName != "" {
|
||||
addr, err := bindIfaceToListenConfig(cfg.interfaceName, lc, network, address)
|
||||
@ -108,7 +118,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
||||
}
|
||||
|
||||
if DisableIPv6 && destination.Is6() {
|
||||
return nil, fmt.Errorf("IPv6 is diabled, dialer cancel")
|
||||
return nil, ErrorDisableIPv6
|
||||
}
|
||||
|
||||
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
||||
@ -230,29 +240,49 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr
|
||||
ip netip.Addr
|
||||
net.Conn
|
||||
error
|
||||
resolved bool
|
||||
isPrimary bool
|
||||
done bool
|
||||
}
|
||||
|
||||
preferCount := atomic.NewInt32(0)
|
||||
results := make(chan dialResult)
|
||||
tcpRacer := func(ctx context.Context, ip netip.Addr) {
|
||||
result := dialResult{ip: ip}
|
||||
result := dialResult{ip: ip, done: true}
|
||||
|
||||
defer func() {
|
||||
select {
|
||||
case results <- result:
|
||||
case <-returned:
|
||||
if result.Conn != nil {
|
||||
result.Conn.Close()
|
||||
_ = result.Conn.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
v := "4"
|
||||
if ip.Is6() {
|
||||
v = "6"
|
||||
if strings.Contains(network, "tcp") {
|
||||
network = "tcp"
|
||||
} else {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
result.Conn, result.error = dialContext(ctx, network+v, ip, port, opt)
|
||||
if ip.Is6() {
|
||||
network += "6"
|
||||
if opt.prefer != 4 {
|
||||
result.isPrimary = true
|
||||
}
|
||||
}
|
||||
|
||||
if ip.Is4() {
|
||||
network += "4"
|
||||
if opt.prefer != 6 {
|
||||
result.isPrimary = true
|
||||
}
|
||||
}
|
||||
|
||||
if result.isPrimary {
|
||||
preferCount.Add(1)
|
||||
}
|
||||
|
||||
result.Conn, result.error = dialContext(ctx, network, ip, port, opt)
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
@ -260,13 +290,28 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr
|
||||
}
|
||||
|
||||
connCount := len(ips)
|
||||
var fallback dialResult
|
||||
for i := 0; i < connCount; i++ {
|
||||
select {
|
||||
case res := <-results:
|
||||
if res.error == nil {
|
||||
return res.Conn, nil
|
||||
if res.isPrimary {
|
||||
return res.Conn, nil
|
||||
} else {
|
||||
fallback = res
|
||||
}
|
||||
} else {
|
||||
if res.isPrimary {
|
||||
preferCount.Add(-1)
|
||||
if preferCount.Load() == 0 && fallback.done {
|
||||
return fallback.Conn, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-ctx.Done():
|
||||
if fallback.done {
|
||||
return fallback.Conn, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -303,25 +348,45 @@ func singleDialContext(ctx context.Context, network string, address string, opt
|
||||
}
|
||||
|
||||
func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp4", "udp4":
|
||||
return concurrentIPv4DialContext(ctx, network, address, opt)
|
||||
default:
|
||||
return concurrentIPv6DialContext(ctx, network, address, opt)
|
||||
}
|
||||
}
|
||||
|
||||
func concurrentIPv4DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []netip.Addr
|
||||
switch network {
|
||||
case "tcp4", "udp4":
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv4ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv4(host)
|
||||
}
|
||||
default:
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv6ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv6(host)
|
||||
}
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv4ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv4(host)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return concurrentDialContext(ctx, network, ips, port, opt)
|
||||
}
|
||||
|
||||
func concurrentIPv6DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []netip.Addr
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv6ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv6(host)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package dialer
|
||||
|
||||
import "go.uber.org/atomic"
|
||||
import (
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultOptions []Option
|
||||
@ -13,6 +15,8 @@ type option struct {
|
||||
addrReuse bool
|
||||
routingMark int
|
||||
direct bool
|
||||
network int
|
||||
prefer int
|
||||
}
|
||||
|
||||
type Option func(opt *option)
|
||||
@ -40,3 +44,25 @@ func WithDirect() Option {
|
||||
opt.direct = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithPreferIPv4() Option {
|
||||
return func(opt *option) {
|
||||
opt.prefer = 4
|
||||
}
|
||||
}
|
||||
|
||||
func WithPreferIPv6() Option {
|
||||
return func(opt *option) {
|
||||
opt.prefer = 6
|
||||
}
|
||||
}
|
||||
|
||||
func WithOnlySingleStack(isIPv4 bool) Option {
|
||||
return func(opt *option) {
|
||||
if isIPv4 {
|
||||
opt.network = 4
|
||||
} else {
|
||||
opt.network = 6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ type Resolver interface {
|
||||
ResolveIPv4(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)
|
||||
}
|
||||
@ -74,10 +73,10 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) {
|
||||
|
||||
// ResolveIPWithResolver same as ResolveIP, but with a resolver
|
||||
func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) {
|
||||
if ips, err := ResolveAllIPPrimaryIPv4WithResolver(host, r); err == nil {
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
if ip, err := ResolveIPv4WithResolver(host, r); err == nil {
|
||||
return ip, nil
|
||||
} else {
|
||||
return netip.Addr{}, err
|
||||
return ResolveIPv6WithResolver(host, r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +94,6 @@ func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
return ResolveIPv4(host)
|
||||
}
|
||||
|
||||
@ -108,7 +106,6 @@ func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
return ResolveIPv6(host)
|
||||
}
|
||||
|
||||
@ -121,7 +118,6 @@ func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
||||
return ip, err
|
||||
}
|
||||
}
|
||||
|
||||
return ResolveIP(host)
|
||||
}
|
||||
|
||||
@ -160,7 +156,6 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||
|
||||
return []netip.Addr{netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))]))}, nil
|
||||
}
|
||||
|
||||
return []netip.Addr{}, ErrIPNotFound
|
||||
}
|
||||
|
||||
@ -200,7 +195,6 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||
|
||||
return []netip.Addr{netip.AddrFrom4(*(*[4]byte)(ip))}, nil
|
||||
}
|
||||
|
||||
return []netip.Addr{}, ErrIPNotFound
|
||||
}
|
||||
|
||||
@ -232,39 +226,6 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||
|
||||
return []netip.Addr{nnip.IpToAddr(ipAddr.IP)}, nil
|
||||
}
|
||||
|
||||
return []netip.Addr{}, ErrIPNotFound
|
||||
}
|
||||
|
||||
func ResolveAllIPPrimaryIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) {
|
||||
if node := DefaultHosts.Search(host); node != nil {
|
||||
return []netip.Addr{node.Data}, nil
|
||||
}
|
||||
|
||||
ip, err := netip.ParseAddr(host)
|
||||
if err == nil {
|
||||
return []netip.Addr{ip}, nil
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
if DisableIPv6 {
|
||||
return r.ResolveAllIPv4(host)
|
||||
}
|
||||
|
||||
return r.ResolveAllIPPrimaryIPv4(host)
|
||||
} else if DisableIPv6 {
|
||||
return ResolveAllIPv4(host)
|
||||
}
|
||||
|
||||
if DefaultResolver == nil {
|
||||
ipAddr, err := net.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return []netip.Addr{}, err
|
||||
}
|
||||
|
||||
return []netip.Addr{nnip.IpToAddr(ipAddr.IP)}, nil
|
||||
}
|
||||
|
||||
return []netip.Addr{}, ErrIPNotFound
|
||||
}
|
||||
|
||||
@ -284,7 +245,6 @@ func ResolveAllIPv6ProxyServerHost(host string) ([]netip.Addr, error) {
|
||||
if ProxyServerHostResolver != nil {
|
||||
return ResolveAllIPv6WithResolver(host, ProxyServerHostResolver)
|
||||
}
|
||||
|
||||
return ResolveAllIPv6(host)
|
||||
}
|
||||
|
||||
@ -292,7 +252,6 @@ func ResolveAllIPv4ProxyServerHost(host string) ([]netip.Addr, error) {
|
||||
if ProxyServerHostResolver != nil {
|
||||
return ResolveAllIPv4WithResolver(host, ProxyServerHostResolver)
|
||||
}
|
||||
|
||||
return ResolveAllIPv4(host)
|
||||
}
|
||||
|
||||
@ -300,6 +259,5 @@ func ResolveAllIPProxyServerHost(host string) ([]netip.Addr, error) {
|
||||
if ProxyServerHostResolver != nil {
|
||||
return ResolveAllIPWithResolver(host, ProxyServerHostResolver)
|
||||
}
|
||||
|
||||
return ResolveAllIP(host)
|
||||
}
|
||||
|
@ -68,3 +68,46 @@ func (e DNSMode) String() string {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type DNSPrefer int
|
||||
|
||||
const (
|
||||
DualStack DNSPrefer = iota
|
||||
IPv4Only
|
||||
IPv6Only
|
||||
IPv4Prefer
|
||||
IPv6Prefer
|
||||
)
|
||||
|
||||
var dnsPreferMap = map[string]DNSPrefer{
|
||||
DualStack.String(): DualStack,
|
||||
IPv4Only.String(): IPv4Only,
|
||||
IPv6Only.String(): IPv6Only,
|
||||
IPv4Prefer.String(): IPv4Prefer,
|
||||
IPv6Prefer.String(): IPv6Prefer,
|
||||
}
|
||||
|
||||
func (d DNSPrefer) String() string {
|
||||
switch d {
|
||||
case DualStack:
|
||||
return "dual"
|
||||
case IPv4Only:
|
||||
return "ipv4"
|
||||
case IPv6Only:
|
||||
return "ipv6"
|
||||
case IPv4Prefer:
|
||||
return "ipv4-prefer"
|
||||
case IPv6Prefer:
|
||||
return "ipv6-prefer"
|
||||
default:
|
||||
return "dual"
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSPrefer(prefer string) DNSPrefer {
|
||||
if p, ok := dnsPreferMap[prefer]; ok {
|
||||
return p
|
||||
} else {
|
||||
return DualStack
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) {
|
||||
return nil, resolver.ErrIPNotFound
|
||||
}
|
||||
ips = append(ips, ipv6s...)
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
case <-time.After(30 * time.Millisecond):
|
||||
// wait ipv6 result
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
# port: 7890 # HTTP(S) 代理服务器端口
|
||||
# socks-port: 7891 # SOCKS5 代理端口
|
||||
mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口
|
||||
# redir-port: 7892 # 透明代理端口 用于Linux和
|
||||
# redir-port: 7892 # 透明代理端口,用于 Linux 和 MacOS
|
||||
|
||||
# Transparent proxy server port for Linux (TProxy TCP and TProxy UDP)
|
||||
# tproxy-port: 7893
|
||||
|
||||
allow-lan: true # 允许局域网连接
|
||||
bind-address: "*" # 绑定IP地址,仅作用于 allow-lan 为true, '*'表示所有地址
|
||||
bind-address: "*" # 绑定IP地址,仅作用于 allow-lan 为 true,'*'表示所有地址
|
||||
|
||||
mode: rule
|
||||
|
||||
log-level: debug #日志等级 silent/error/warning/info/debug
|
||||
log-level: debug # 日志等级 silent/error/warning/info/debug
|
||||
|
||||
ipv6: true #开启IPv6总开关 关闭阻断所有IPv6和屏蔽DNS请求AAAA记录
|
||||
ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录
|
||||
|
||||
external-controller: 0.0.0.0:9093 # RESTful API 监听地址
|
||||
|
||||
@ -24,33 +24,33 @@ external-ui: /path/to/ui/folder # 配置WEB UI目录,使用http://{{external-c
|
||||
|
||||
# interface-name: en0 # 设置出口网卡
|
||||
|
||||
# routing-mark: 6666 # 配置fwmark 仅用于Linux
|
||||
# routing-mark: 6666 # 配置 fwmark 仅用于Linux
|
||||
experimental:
|
||||
# 具体配置待定
|
||||
# 证书指纹,SHA256格式,补充校验TLS证书
|
||||
# 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取
|
||||
fingerprints:
|
||||
- "8F111FA9AD3CD8E917A118522CAC39EA33741B3BBE73F91CECE548D5CCB0E5E8" # 忽略大小写
|
||||
# 类似于/etc/hosts, 仅支持配置单个IP
|
||||
# 类似于 /etc/hosts, 仅支持配置单个 IP
|
||||
hosts:
|
||||
# '*.clash.dev': 127.0.0.1
|
||||
# '.dev': 127.0.0.1
|
||||
# 'alpha.clash.dev': '::1'
|
||||
|
||||
# Tun配置
|
||||
# Tun 配置
|
||||
tun:
|
||||
enable: false
|
||||
stack: system # gvisor
|
||||
dns-hijack:
|
||||
- 198.18.0.2:53 # 需要劫持的DNS
|
||||
# auto-detect-interface: true # 自动识别interface
|
||||
# auto-route: true # 配置
|
||||
- 198.18.0.2:53 # 需要劫持的 DNS
|
||||
# auto-detect-interface: true # 自动识别出口网卡
|
||||
# auto-route: true # 配置路由表
|
||||
|
||||
#ebpf配置
|
||||
ebpf:
|
||||
auto-redir: #redirect模式,仅支持TCP
|
||||
auto-redir: # redirect 模式,仅支持 TCP
|
||||
- eth0
|
||||
redirect-to-tun: #UDP+TCP,使用该功能请勿启用auto-route
|
||||
redirect-to-tun: # UDP+TCP 使用该功能请勿启用 auto-route
|
||||
- eth0
|
||||
|
||||
# 嗅探域名 可选配置
|
||||
@ -63,7 +63,7 @@ sniffer:
|
||||
# 强制对此域名进行嗅探
|
||||
force-domain:
|
||||
- +.v2ex.com
|
||||
# 仅对白名单中的端口进行嗅探,默认为0-65535,推荐只对需要嗅探写的的常见端口嗅探
|
||||
# 仅对白名单中的端口进行嗅探,默认为 443,80
|
||||
port-whitelist:
|
||||
- "80"
|
||||
- "443"
|
||||
@ -78,12 +78,12 @@ profile:
|
||||
|
||||
# DNS配置
|
||||
dns:
|
||||
enable: false # 关闭将使用系统DNS
|
||||
listen: 0.0.0.0:53 # 开启DNS服务器监听
|
||||
# ipv6: false # false将返回AAAA的空结果
|
||||
enable: false # 关闭将使用系统 DNS
|
||||
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
|
||||
# ipv6: false # false 将返回 AAAA 的空结果
|
||||
|
||||
# 用于解析nameserver,fallbacky以及其他DNS服务器配置的,DNS服务域名
|
||||
# 只能使用纯IP地址,可使用加密DNS
|
||||
# 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名
|
||||
# 只能使用纯 IP 地址,可使用加密 DNS
|
||||
default-nameserver:
|
||||
- 114.114.114.114
|
||||
- 8.8.8.8
|
||||
@ -93,7 +93,7 @@ dns:
|
||||
|
||||
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
|
||||
|
||||
# use-hosts: true # 查询hosts
|
||||
# use-hosts: true # 查询 hosts
|
||||
|
||||
# 配置不使用fake-ip的域名
|
||||
# fake-ip-filter:
|
||||
@ -101,40 +101,40 @@ dns:
|
||||
# - localhost.ptlogin2.qq.com
|
||||
|
||||
# DNS主要域名配置
|
||||
# 支持 UDP,TCP,DoT,DoH,DoQ
|
||||
# 这部分为主要DNS配置,影响所有直连,确保使用对大陆解析精准的DNS
|
||||
# 支持 UDP,TCP,DoT,DoH,DoQ
|
||||
# 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS
|
||||
nameserver:
|
||||
- 114.114.114.114 # default value
|
||||
- 8.8.8.8 # default value
|
||||
- tls://223.5.5.5:853 # DNS over TLS
|
||||
- https://doh.pub/dns-query # DNS over HTTPS
|
||||
- https://dns.alidns.com/dns-query#h3=true # 强制HTTP/3
|
||||
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用HTTP/3
|
||||
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
|
||||
- dhcp://en0 # dns from dhcp
|
||||
- quic://dns.adguard.com:784 # DNS over QUIC
|
||||
# - '8.8.8.8#en0' # 兼容指定DNS出口网卡
|
||||
|
||||
# 当配置fallback时,会查询nameserver中返回的IP是否为CN,非必要配置
|
||||
# 当不是CN,则使用fallback中的DNS查询结果
|
||||
# 确保配置fallback时能够正常查询
|
||||
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置
|
||||
# 当不是 CN,则使用 fallback 中的 DNS 查询结果
|
||||
# 确保配置 fallback 时能够正常查询
|
||||
# fallback:
|
||||
# - tcp://1.1.1.1
|
||||
# - 'tcp://1.1.1.1#ProxyGroupName' # 指定DNS过代理查询,ProxyGroupName为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡
|
||||
# - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡
|
||||
|
||||
# 专用于节点域名解析的DNS服务器,非必要配置项
|
||||
# 配置服务器若查询失败将使用nameserver,非并发查询
|
||||
# 专用于节点域名解析的 DNS 服务器,非必要配置项
|
||||
# 配置服务器若查询失败将使用 nameserver,非并发查询
|
||||
# proxy-server-nameserver:
|
||||
# - https://dns.google/dns-query
|
||||
# - tls://one.one.one.one
|
||||
|
||||
# 配置fallback使用条件
|
||||
# 配置 fallback 使用条件
|
||||
# fallback-filter:
|
||||
# geoip: true # 配置是否使用geoip
|
||||
# geoip-code: CN # 当nameserver域名的IP查询geoip库为CN时,不使用fallback
|
||||
# 配置强制fallback,优先于IP判断,具体分类自行查看geosite库
|
||||
# geoip: true # 配置是否使用 geoip
|
||||
# geoip-code: CN # 当 nameserver 域名的 IP 查询 geoip 库为 CN 时,不使用 fallback 中的 DNS 查询结果
|
||||
# 配置强制 fallback,优先于 IP 判断,具体分类自行查看 geosite 库
|
||||
# geosite:
|
||||
# - gfw
|
||||
# 配置不需要使用fallback的IP CIDR
|
||||
# 配置不需要使用 fallback 的 IP CIDR
|
||||
# ipcidr:
|
||||
# - 240.0.0.0/4
|
||||
# domain:
|
||||
@ -142,7 +142,7 @@ dns:
|
||||
# - '+.facebook.com'
|
||||
# - '+.youtube.com'
|
||||
|
||||
# 配置查询域名使用的DNS服务器
|
||||
# 配置查询域名使用的 DNS 服务器
|
||||
# nameserver-policy:
|
||||
# 'www.baidu.com': '114.114.114.114'
|
||||
# '+.internal.crop.com': '10.0.0.1'
|
||||
@ -164,7 +164,12 @@ proxies:
|
||||
password: "password"
|
||||
# udp: true
|
||||
# udp-over-tcp: false
|
||||
|
||||
# ip-version: ipv4 # 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer。默认使用 dual
|
||||
# ipv4:仅使用 IPv4 ipv6:仅使用 IPv6
|
||||
# ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接,
|
||||
# UDP 则为双栈解析,获取结果中的第一个 IPv4
|
||||
# ipv6-prefer 同 ipv4-prefer
|
||||
# 现有协议都支持此参数
|
||||
- name: "ss2"
|
||||
type: ss
|
||||
server: server
|
||||
@ -249,6 +254,7 @@ proxies:
|
||||
# # headers:
|
||||
# # Connection:
|
||||
# # - keep-alive
|
||||
# ip-version: ipv4 # 设置使用 IP 类型偏好,可选:ipv4,ipv6,dual,默认值:dual
|
||||
|
||||
- name: vmess-grpc
|
||||
server: server
|
||||
@ -264,6 +270,7 @@ proxies:
|
||||
# skip-cert-verify: true
|
||||
grpc-opts:
|
||||
grpc-service-name: "example"
|
||||
# ip-version: ipv4
|
||||
|
||||
# socks5
|
||||
- name: "socks"
|
||||
@ -276,6 +283,7 @@ proxies:
|
||||
# fingerprint: xxxx
|
||||
# skip-cert-verify: true
|
||||
# udp: true
|
||||
# ip-version: ipv6
|
||||
|
||||
# http
|
||||
- name: "http"
|
||||
@ -288,6 +296,7 @@ proxies:
|
||||
# skip-cert-verify: true
|
||||
# sni: custom.com
|
||||
# fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints
|
||||
# ip-version: dual
|
||||
|
||||
# Snell
|
||||
# Beware that there's currently no UDP support yet
|
||||
@ -389,9 +398,9 @@ proxies:
|
||||
auth_str: yourpassword
|
||||
# obfs: obfs_str
|
||||
# alpn: h3
|
||||
protocol: udp #支持udp/wechat-video/faketcp
|
||||
up: "30 Mbps" #若不写单位,默认为Mbps
|
||||
down: "200 Mbps" #若不写单位,默认为Mbps
|
||||
protocol: udp # 支持 udp/wechat-video/faketcp
|
||||
up: "30 Mbps" # 若不写单位,默认为 Mbps
|
||||
down: "200 Mbps" # 若不写单位,默认为 Mbps
|
||||
#sni: server.com
|
||||
#skip-cert-verify: false
|
||||
#recv_window_conn: 12582912
|
||||
@ -422,7 +431,7 @@ proxies:
|
||||
# udp: true
|
||||
|
||||
proxy-groups:
|
||||
# 代理链,若落地协议支持UDP over TCP则可支持UDP
|
||||
# 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
|
||||
# Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
|
||||
- name: "relay"
|
||||
type: relay
|
||||
@ -432,7 +441,7 @@ proxy-groups:
|
||||
- ss1
|
||||
- ss2
|
||||
|
||||
# url-test 将按照url测试结果使用延迟最低节点
|
||||
# url-test 将按照 url 测试结果使用延迟最低节点
|
||||
- name: "auto"
|
||||
type: url-test
|
||||
proxies:
|
||||
@ -444,7 +453,7 @@ proxy-groups:
|
||||
url: "http://www.gstatic.com/generate_204"
|
||||
interval: 300
|
||||
|
||||
# fallback 将按照url测试结果按照节点顺序选择
|
||||
# fallback 将按照 url 测试结果按照节点顺序选择
|
||||
- name: "fallback-auto"
|
||||
type: fallback
|
||||
proxies:
|
||||
@ -475,7 +484,7 @@ proxy-groups:
|
||||
- vmess1
|
||||
- auto
|
||||
|
||||
# 配置指定interface-name和fwmark的DIRECT
|
||||
# 配置指定 interface-name 和 fwmark 的 DIRECT
|
||||
- name: en1
|
||||
type: select
|
||||
interface-name: en1
|
||||
@ -485,14 +494,14 @@ proxy-groups:
|
||||
|
||||
- name: UseProvider
|
||||
type: select
|
||||
filter: "HK|TW" # 正则表达式,过滤provider1中节点名包含HK或TW
|
||||
filter: "HK|TW" # 正则表达式,过滤 provider1 中节点名包含 HK 或 TW
|
||||
use:
|
||||
- provider1
|
||||
proxies:
|
||||
- Proxy
|
||||
- DIRECT
|
||||
|
||||
# Clash格式的节点或支持*ray的分享格式
|
||||
# Clash 格式的节点或支持 *ray 的分享格式
|
||||
proxy-providers:
|
||||
provider1:
|
||||
type: http
|
||||
|
@ -4,12 +4,10 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/transport/hysteria/conns/faketcp"
|
||||
"github.com/Dreamacro/clash/transport/hysteria/conns/udp"
|
||||
"github.com/Dreamacro/clash/transport/hysteria/conns/wechat"
|
||||
"github.com/Dreamacro/clash/transport/hysteria/obfs"
|
||||
"github.com/Dreamacro/clash/transport/hysteria/utils"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"net"
|
||||
)
|
||||
@ -61,29 +59,21 @@ func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs
|
||||
type PacketDialer interface {
|
||||
ListenPacket() (net.PacketConn, error)
|
||||
Context() context.Context
|
||||
RemoteAddr(host string) (net.Addr, error)
|
||||
}
|
||||
|
||||
func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfs.Obfuscator, dialer PacketDialer) (quic.Connection, error) {
|
||||
ipStr, port, err := utils.SplitHostPort(server)
|
||||
serverUDPAddr, err := dialer.RemoteAddr(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ip, err := resolver.ResolveProxyServerHost(ipStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverUDPAddr := net.UDPAddr{
|
||||
IP: net.ParseIP(ip.String()),
|
||||
Port: int(port),
|
||||
}
|
||||
|
||||
pktConn, err := ct.quicPacketConn(proto, serverUDPAddr.String(), obfs, dialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qs, err := quic.DialContext(dialer.Context(), pktConn, &serverUDPAddr, server, tlsConfig, quicConfig)
|
||||
|
||||
qs, err := quic.DialContext(dialer.Context(), pktConn, serverUDPAddr, server, tlsConfig, quicConfig)
|
||||
if err != nil {
|
||||
_ = pktConn.Close()
|
||||
return nil, err
|
||||
|
Loading…
Reference in New Issue
Block a user