mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-04-22 16:17:16 +08:00
feat: support ipv6 dual stack fallback for tuic
This commit is contained in:
+13
-33
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/metacubex/mihomo/component/ech"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/transport/tuic"
|
||||
"github.com/metacubex/mihomo/transport/tuic/common"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/metacubex/quic-go"
|
||||
@@ -25,8 +26,9 @@ type Tuic struct {
|
||||
option *TuicOption
|
||||
client *tuic.PoolClient
|
||||
|
||||
tlsConfig *tls.Config
|
||||
echConfig *ech.Config
|
||||
quicConfig *quic.Config
|
||||
tlsConfig *tls.Config
|
||||
echConfig *ech.Config
|
||||
}
|
||||
|
||||
type TuicOption struct {
|
||||
@@ -106,25 +108,13 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_
|
||||
return newPacketConn(pc, t), nil
|
||||
}
|
||||
|
||||
func (t *Tuic) dial(ctx context.Context) (transport *quic.Transport, addr net.Addr, err error) {
|
||||
udpAddr, err := resolveUDPAddr(ctx, "udp", t.addr, t.prefer)
|
||||
func (t *Tuic) dial(ctx context.Context) (quicConn *quic.Conn, err error) {
|
||||
_, quicConn, err = common.DialQuic(ctx, t.addr, t.DialOptions(), t.dialer, t.tlsConfig, t.quicConfig, t.option.ReduceRtt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
err = t.echConfig.ClientHandle(ctx, t.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
addr = udpAddr
|
||||
var pc net.PacketConn
|
||||
pc, err = t.dialer.ListenPacket(ctx, "udp", "", udpAddr.AddrPort())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
transport = &quic.Transport{Conn: pc}
|
||||
transport.SetCreatedConn(true) // auto close conn
|
||||
transport.SetSingleUse(true) // auto close transport
|
||||
return
|
||||
common.SetCongestionController(quicConn, t.option.CongestionController, t.option.CWND)
|
||||
return quicConn, nil
|
||||
}
|
||||
|
||||
// ProxyInfo implements C.ProxyAdapter
|
||||
@@ -232,7 +222,6 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
||||
tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config
|
||||
}
|
||||
|
||||
tlsClientConfig := tlsConfig
|
||||
echConfig, err := option.ECHOpts.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -258,9 +247,10 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
||||
rmark: option.RoutingMark,
|
||||
prefer: option.IPVersion,
|
||||
},
|
||||
option: &option,
|
||||
tlsConfig: tlsClientConfig,
|
||||
echConfig: echConfig,
|
||||
option: &option,
|
||||
quicConfig: quicConfig,
|
||||
tlsConfig: tlsConfig,
|
||||
echConfig: echConfig,
|
||||
}
|
||||
t.dialer = option.NewDialer(t.DialOptions())
|
||||
|
||||
@@ -278,17 +268,12 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
||||
if len(option.Token) > 0 {
|
||||
tkn := tuic.GenTKN(option.Token)
|
||||
clientOption := &tuic.ClientOptionV4{
|
||||
TlsConfig: tlsClientConfig,
|
||||
QuicConfig: quicConfig,
|
||||
Token: tkn,
|
||||
UdpRelayMode: udpRelayMode,
|
||||
CongestionController: option.CongestionController,
|
||||
ReduceRtt: option.ReduceRtt,
|
||||
RequestTimeout: time.Duration(option.RequestTimeout) * time.Millisecond,
|
||||
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
|
||||
FastOpen: option.FastOpen,
|
||||
MaxOpenStreams: clientMaxOpenStreams,
|
||||
CWND: option.CWND,
|
||||
}
|
||||
|
||||
t.client = tuic.NewPoolClientV4(clientOption, t.dial)
|
||||
@@ -298,16 +283,11 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
||||
maxUdpRelayPacketSize = tuic.MaxFragSizeV5
|
||||
}
|
||||
clientOption := &tuic.ClientOptionV5{
|
||||
TlsConfig: tlsClientConfig,
|
||||
QuicConfig: quicConfig,
|
||||
Uuid: uuid.FromStringOrNil(option.UUID),
|
||||
Password: option.Password,
|
||||
UdpRelayMode: udpRelayMode,
|
||||
CongestionController: option.CongestionController,
|
||||
ReduceRtt: option.ReduceRtt,
|
||||
MaxUdpRelayPacketSize: maxUdpRelayPacketSize,
|
||||
MaxOpenStreams: clientMaxOpenStreams,
|
||||
CWND: option.CWND,
|
||||
}
|
||||
|
||||
t.client = tuic.NewPoolClientV5(clientOption, t.dial)
|
||||
|
||||
@@ -18,7 +18,7 @@ var (
|
||||
TooManyOpenStreams = errors.New("tuic: too many open streams")
|
||||
)
|
||||
|
||||
type DialFunc func(ctx context.Context) (transport *quic.Transport, addr net.Addr, err error)
|
||||
type DialFunc func(ctx context.Context) (quicConn *quic.Conn, err error)
|
||||
|
||||
type Client interface {
|
||||
DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error)
|
||||
|
||||
@@ -4,16 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
)
|
||||
@@ -22,7 +17,7 @@ type PoolClient struct {
|
||||
newClientOptionV4 *ClientOptionV4
|
||||
newClientOptionV5 *ClientOptionV5
|
||||
|
||||
dialHelper *poolDialHelper
|
||||
dialFn DialFunc
|
||||
tcpClients list.List[Client]
|
||||
tcpClientsMutex sync.Mutex
|
||||
udpClients list.List[Client]
|
||||
@@ -51,47 +46,6 @@ func (t *PoolClient) ListenPacket(ctx context.Context, metadata *C.Metadata) (ne
|
||||
return N.NewRefPacketConn(pc, t), nil
|
||||
}
|
||||
|
||||
// poolDialHelper is a helper for dialFn
|
||||
// using a standalone struct to let finalizer working
|
||||
type poolDialHelper struct {
|
||||
dialFn DialFunc
|
||||
dialResult atomic.Pointer[dialResult]
|
||||
}
|
||||
|
||||
type dialResult struct {
|
||||
transport *quic.Transport
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
func (t *poolDialHelper) dial(ctx context.Context) (transport *quic.Transport, addr net.Addr, err error) {
|
||||
if dr := t.dialResult.Load(); dr != nil {
|
||||
return dr.transport, dr.addr, nil
|
||||
}
|
||||
|
||||
transport, addr, err = t.dialFn(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if _, ok := transport.Conn.(*net.UDPConn); ok { // only cache the system's UDPConn
|
||||
transport.SetSingleUse(false) // don't close transport in each dial
|
||||
|
||||
dr := &dialResult{transport: transport, addr: addr}
|
||||
t.dialResult.Store(dr)
|
||||
}
|
||||
|
||||
return transport, addr, err
|
||||
}
|
||||
|
||||
func (t *poolDialHelper) forceClose() {
|
||||
if dr := t.dialResult.Swap(nil); dr != nil {
|
||||
transport := dr.transport
|
||||
if transport != nil {
|
||||
_ = transport.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *PoolClient) newClient(udp bool) (client Client) {
|
||||
clients := &t.tcpClients
|
||||
clientsMutex := &t.tcpClientsMutex
|
||||
@@ -103,11 +57,10 @@ func (t *PoolClient) newClient(udp bool) (client Client) {
|
||||
clientsMutex.Lock()
|
||||
defer clientsMutex.Unlock()
|
||||
|
||||
dialHelper := t.dialHelper
|
||||
if t.newClientOptionV4 != nil {
|
||||
client = NewClientV4(t.newClientOptionV4, udp, dialHelper.dial)
|
||||
client = NewClientV4(t.newClientOptionV4, udp, t.dialFn)
|
||||
} else {
|
||||
client = NewClientV5(t.newClientOptionV5, udp, dialHelper.dial)
|
||||
client = NewClientV5(t.newClientOptionV5, udp, t.dialFn)
|
||||
}
|
||||
|
||||
client.SetLastVisited(time.Now())
|
||||
@@ -168,27 +121,18 @@ func (t *PoolClient) getClient(udp bool) Client {
|
||||
|
||||
func NewPoolClientV4(clientOption *ClientOptionV4, dialFn DialFunc) *PoolClient {
|
||||
p := &PoolClient{
|
||||
dialHelper: &poolDialHelper{dialFn: dialFn},
|
||||
dialFn: dialFn,
|
||||
}
|
||||
newClientOption := *clientOption
|
||||
p.newClientOptionV4 = &newClientOption
|
||||
runtime.SetFinalizer(p, closeClientPool)
|
||||
log.Debugln("New TuicV4 PoolClient at %p", p)
|
||||
return p
|
||||
}
|
||||
|
||||
func NewPoolClientV5(clientOption *ClientOptionV5, dialFn DialFunc) *PoolClient {
|
||||
p := &PoolClient{
|
||||
dialHelper: &poolDialHelper{dialFn: dialFn},
|
||||
dialFn: dialFn,
|
||||
}
|
||||
newClientOption := *clientOption
|
||||
p.newClientOptionV5 = &newClientOption
|
||||
runtime.SetFinalizer(p, closeClientPool)
|
||||
log.Debugln("New TuicV5 PoolClient at %p", p)
|
||||
return p
|
||||
}
|
||||
|
||||
func closeClientPool(client *PoolClient) {
|
||||
log.Debugln("Close Tuic PoolClient at %p", client)
|
||||
client.dialHelper.forceClose()
|
||||
}
|
||||
|
||||
@@ -21,21 +21,15 @@ import (
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/metacubex/tls"
|
||||
)
|
||||
|
||||
type ClientOption struct {
|
||||
TlsConfig *tls.Config
|
||||
QuicConfig *quic.Config
|
||||
Token [32]byte
|
||||
UdpRelayMode common.UdpRelayMode
|
||||
CongestionController string
|
||||
ReduceRtt bool
|
||||
RequestTimeout time.Duration
|
||||
MaxUdpRelayPacketSize int
|
||||
FastOpen bool
|
||||
MaxOpenStreams int64
|
||||
CWND int
|
||||
}
|
||||
|
||||
type clientImpl struct {
|
||||
@@ -73,21 +67,10 @@ func (t *clientImpl) getQuicConn(ctx context.Context) (*quic.Conn, error) {
|
||||
if t.quicConn != nil {
|
||||
return t.quicConn, nil
|
||||
}
|
||||
transport, addr, err := t.dialFn(ctx)
|
||||
quicConn, err := t.dialFn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var quicConn *quic.Conn
|
||||
if t.ReduceRtt {
|
||||
quicConn, err = transport.DialEarly(ctx, addr, t.TlsConfig, t.QuicConfig)
|
||||
} else {
|
||||
quicConn, err = transport.Dial(ctx, addr, t.TlsConfig, t.QuicConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
common.SetCongestionController(quicConn, t.CongestionController, t.CWND)
|
||||
|
||||
go func() {
|
||||
_ = t.sendAuthentication(quicConn)
|
||||
|
||||
@@ -21,20 +21,14 @@ import (
|
||||
|
||||
"github.com/metacubex/quic-go"
|
||||
"github.com/metacubex/randv2"
|
||||
"github.com/metacubex/tls"
|
||||
)
|
||||
|
||||
type ClientOption struct {
|
||||
TlsConfig *tls.Config
|
||||
QuicConfig *quic.Config
|
||||
Uuid [16]byte
|
||||
Password string
|
||||
UdpRelayMode common.UdpRelayMode
|
||||
CongestionController string
|
||||
ReduceRtt bool
|
||||
MaxUdpRelayPacketSize int
|
||||
MaxOpenStreams int64
|
||||
CWND int
|
||||
}
|
||||
|
||||
type clientImpl struct {
|
||||
@@ -72,21 +66,10 @@ func (t *clientImpl) getQuicConn(ctx context.Context) (*quic.Conn, error) {
|
||||
if t.quicConn != nil {
|
||||
return t.quicConn, nil
|
||||
}
|
||||
transport, addr, err := t.dialFn(ctx)
|
||||
quicConn, err := t.dialFn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var quicConn *quic.Conn
|
||||
if t.ReduceRtt {
|
||||
quicConn, err = transport.DialEarly(ctx, addr, t.TlsConfig, t.QuicConfig)
|
||||
} else {
|
||||
quicConn, err = transport.Dial(ctx, addr, t.TlsConfig, t.QuicConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
common.SetCongestionController(quicConn, t.CongestionController, t.CWND)
|
||||
|
||||
go func() {
|
||||
_ = t.sendAuthentication(quicConn)
|
||||
|
||||
Reference in New Issue
Block a user