feat: add ech-opts for anytls/shadowsocks/trojan/vmess/vless outbound

This commit is contained in:
wwqgtxx
2025-05-17 13:53:21 +08:00
parent bb8c47d83d
commit c6d7ef8cb8
16 changed files with 271 additions and 30 deletions

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/transport/vmess"
smux "github.com/metacubex/smux"
)
@@ -18,6 +19,7 @@ type Option struct {
Path string
Headers map[string]string
TLS bool
ECHConfig *ech.Config
SkipCertVerify bool
Fingerprint string
Mux bool
@@ -48,10 +50,11 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C
}
config := &vmess.WebsocketConfig{
Host: option.Host,
Port: option.Port,
Path: option.Path,
Headers: header,
Host: option.Host,
Port: option.Port,
Path: option.Path,
ECHConfig: option.ECHConfig,
Headers: header,
}
if option.TLS {

View File

@@ -21,6 +21,7 @@ import (
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/common/buf"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/component/ech"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
@@ -224,7 +225,7 @@ func (g *Conn) SetDeadline(t time.Time) error {
return nil
}
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint string, realityConfig *tlsC.RealityConfig) *TransportWrap {
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint string, echConfig *ech.Config, realityConfig *tlsC.RealityConfig) *TransportWrap {
dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
defer cancel()
@@ -238,8 +239,15 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri
}
if clientFingerprint, ok := tlsC.GetFingerprint(clientFingerprint); ok {
tlsConfig := tlsC.UConfig(tlsConfig)
err := echConfig.ClientHandle(ctx, tlsConfig)
if err != nil {
pconn.Close()
return nil, err
}
if realityConfig == nil {
tlsConn := tlsC.UClient(pconn, tlsC.UConfig(cfg), clientFingerprint)
tlsConn := tlsC.UClient(pconn, tlsConfig, clientFingerprint)
if err := tlsConn.HandshakeContext(ctx); err != nil {
pconn.Close()
return nil, err
@@ -251,7 +259,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri
}
return tlsConn, nil
} else {
realityConn, err := tlsC.GetRealityConn(ctx, pconn, clientFingerprint, cfg, realityConfig)
realityConn, err := tlsC.GetRealityConn(ctx, pconn, clientFingerprint, tlsConfig, realityConfig)
if err != nil {
pconn.Close()
return nil, err
@@ -268,6 +276,27 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri
return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint")
}
if echConfig != nil {
tlsConfig := tlsC.UConfig(tlsConfig)
err := echConfig.ClientHandle(ctx, tlsConfig)
if err != nil {
pconn.Close()
return nil, err
}
conn := tlsC.Client(pconn, tlsConfig)
if err := conn.HandshakeContext(ctx); err != nil {
pconn.Close()
return nil, err
}
state := conn.ConnectionState()
if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
conn.Close()
return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS)
}
return conn, nil
}
conn := tls.Client(pconn, cfg)
if err := conn.HandshakeContext(ctx); err != nil {
pconn.Close()
@@ -345,12 +374,12 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
return conn, nil
}
func StreamGunWithConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config, realityConfig *tlsC.RealityConfig) (net.Conn, error) {
func StreamGunWithConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config, echConfig *ech.Config, realityConfig *tlsC.RealityConfig) (net.Conn, error) {
dialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {
return conn, nil
}
transport := NewHTTP2Client(dialFn, tlsConfig, cfg.ClientFingerprint, realityConfig)
transport := NewHTTP2Client(dialFn, tlsConfig, cfg.ClientFingerprint, echConfig, realityConfig)
c, err := StreamGunWithTransport(transport, cfg)
if err != nil {
return nil, err

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/transport/vmess"
)
@@ -17,6 +18,7 @@ type Option struct {
Path string
Headers map[string]string
TLS bool
ECHConfig *ech.Config
SkipCertVerify bool
Fingerprint string
Mux bool
@@ -37,6 +39,7 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
Path: option.Path,
V2rayHttpUpgrade: option.V2rayHttpUpgrade,
V2rayHttpUpgradeFastOpen: option.V2rayHttpUpgradeFastOpen,
ECHConfig: option.ECHConfig,
Headers: header,
}

View File

@@ -7,6 +7,7 @@ import (
"net"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/ech"
tlsC "github.com/metacubex/mihomo/component/tls"
)
@@ -16,9 +17,14 @@ type TLSConfig struct {
FingerPrint string
ClientFingerprint string
NextProtos []string
ECH *ech.Config
Reality *tlsC.RealityConfig
}
type ECHConfig struct {
Enable bool
}
func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConfig := &tls.Config{
ServerName: cfg.Host,
@@ -33,8 +39,14 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
}
if clientFingerprint, ok := tlsC.GetFingerprint(cfg.ClientFingerprint); ok {
tlsConfig := tlsC.UConfig(tlsConfig)
err = cfg.ECH.ClientHandle(ctx, tlsConfig)
if err != nil {
return nil, err
}
if cfg.Reality == nil {
tlsConn := tlsC.UClient(conn, tlsC.UConfig(tlsConfig), clientFingerprint)
tlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint)
err = tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
@@ -48,6 +60,19 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint")
}
if cfg.ECH != nil {
tlsConfig := tlsC.UConfig(tlsConfig)
err = cfg.ECH.ClientHandle(ctx, tlsConfig)
if err != nil {
return nil, err
}
tlsConn := tlsC.Client(conn, tlsConfig)
err = tlsConn.HandshakeContext(ctx)
return tlsConn, err
}
tlsConn := tls.Client(conn, tlsConfig)
err = tlsConn.HandshakeContext(ctx)

View File

@@ -21,6 +21,7 @@ import (
"github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/ech"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/log"
@@ -56,6 +57,7 @@ type WebsocketConfig struct {
Headers http.Header
TLS bool
TLSConfig *tls.Config
ECHConfig *ech.Config
MaxEarlyData int
EarlyDataHeaderName string
ClientFingerprint string
@@ -355,6 +357,11 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig,
}
if clientFingerprint, ok := tlsC.GetFingerprint(c.ClientFingerprint); ok {
tlsConfig := tlsC.UConfig(config)
err = c.ECHConfig.ClientHandle(ctx, tlsConfig)
if err != nil {
return nil, err
}
tlsConn := tlsC.UClient(conn, tlsC.UConfig(config), clientFingerprint)
if err = tlsC.BuildWebsocketHandshakeState(tlsConn); err != nil {
return nil, fmt.Errorf("parse url %s error: %w", c.Path, err)
@@ -364,6 +371,16 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig,
return nil, err
}
conn = tlsConn
} else if c.ECHConfig != nil {
tlsConfig := tlsC.UConfig(config)
err = c.ECHConfig.ClientHandle(ctx, tlsConfig)
if err != nil {
return nil, err
}
tlsConn := tlsC.Client(conn, tlsConfig)
err = tlsConn.HandshakeContext(ctx)
conn = tlsConn
} else {
tlsConn := tls.Client(conn, config)
err = tlsConn.HandshakeContext(ctx)