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