chore: cleanup trojan code

This commit is contained in:
wwqgtxx 2025-04-18 16:59:28 +08:00
parent bad61f918f
commit 63e66f49ca
2 changed files with 56 additions and 155 deletions

View file

@ -18,12 +18,13 @@ import (
"github.com/metacubex/mihomo/transport/gun"
"github.com/metacubex/mihomo/transport/shadowsocks/core"
"github.com/metacubex/mihomo/transport/trojan"
"github.com/metacubex/mihomo/transport/vmess"
)
type Trojan struct {
*Base
instance *trojan.Trojan
option *TrojanOption
option *TrojanOption
hexPassword [trojan.KeyLength]byte
// for gun mux
gunTLSConfig *tls.Config
@ -62,14 +63,20 @@ type TrojanSSOption struct {
}
func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) {
var err error
if t.option.Network == "ws" {
host, port, _ := net.SplitHostPort(t.addr)
wsOpts := &trojan.WebsocketOption{
wsOpts := &vmess.WebsocketConfig{
Host: host,
Port: port,
Path: t.option.WSOpts.Path,
MaxEarlyData: t.option.WSOpts.MaxEarlyData,
EarlyDataHeaderName: t.option.WSOpts.EarlyDataHeaderName,
V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade,
V2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen,
ClientFingerprint: t.option.ClientFingerprint,
Headers: http.Header{},
}
@ -83,10 +90,39 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error)
}
}
return t.instance.StreamWebsocketConn(ctx, c, wsOpts)
alpn := trojan.DefaultWebsocketALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
}
wsOpts.TLS = true
tlsConfig := &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.SNI,
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
if err != nil {
return nil, err
}
return vmess.StreamWebsocketConn(ctx, c, wsOpts)
}
return t.instance.StreamConn(ctx, c)
alpn := trojan.DefaultALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
}
return vmess.StreamTLSConn(ctx, c, &vmess.TLSConfig{
Host: t.option.SNI,
SkipCertVerify: t.option.SkipCertVerify,
FingerPrint: t.option.Fingerprint,
ClientFingerprint: t.option.ClientFingerprint,
NextProtos: alpn,
Reality: t.realityConfig,
})
}
// StreamConnContext implements C.ProxyAdapter
@ -124,7 +160,7 @@ func (t *Trojan) writeHeaderContext(ctx context.Context, c net.Conn, metadata *C
if metadata.NetWork == C.UDP {
command = trojan.CommandUDP
}
err = t.instance.WriteHeader(c, command, serializesSocksAddr(metadata))
err = trojan.WriteHeader(c, t.hexPassword, command, serializesSocksAddr(metadata))
return err
}
@ -199,7 +235,7 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
return nil, err
}
pc := t.instance.PacketConn(c)
pc := trojan.NewPacketConn(c)
return newPacketConn(pc, t), err
}
return t.ListenPacketWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata)
@ -234,7 +270,7 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
return nil, err
}
pc := t.instance.PacketConn(c)
pc := trojan.NewPacketConn(c)
return newPacketConn(pc, t), err
}
@ -245,7 +281,7 @@ func (t *Trojan) SupportWithDialer() C.NetWork {
// ListenPacketOnStreamConn implements C.ProxyAdapter
func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
pc := t.instance.PacketConn(c)
pc := trojan.NewPacketConn(c)
return newPacketConn(pc, t), err
}
@ -272,19 +308,6 @@ func (t *Trojan) Close() error {
func NewTrojan(option TrojanOption) (*Trojan, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
tOption := &trojan.Option{
Password: option.Password,
ALPN: option.ALPN,
ServerName: option.Server,
SkipCertVerify: option.SkipCertVerify,
Fingerprint: option.Fingerprint,
ClientFingerprint: option.ClientFingerprint,
}
if option.SNI != "" {
tOption.ServerName = option.SNI
}
t := &Trojan{
Base: &Base{
name: option.Name,
@ -297,8 +320,8 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
instance: trojan.New(tOption),
option: &option,
option: &option,
hexPassword: trojan.Key(option.Password),
}
var err error
@ -306,7 +329,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
if err != nil {
return nil, err
}
tOption.Reality = t.realityConfig
if option.SSOpts.Enabled {
if option.SSOpts.Password == "" {
@ -342,8 +364,8 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
tlsConfig := &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: tOption.SkipCertVerify,
ServerName: tOption.ServerName,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.SNI,
}
var err error
@ -352,13 +374,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
return nil, err
}
t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, tOption.ClientFingerprint, t.realityConfig)
t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, option.ClientFingerprint, t.realityConfig)
t.gunTLSConfig = tlsConfig
t.gunConfig = &gun.Config{
ServiceName: option.GrpcOpts.GrpcServiceName,
Host: tOption.ServerName,
ClientFingerprint: tOption.ClientFingerprint,
Host: option.SNI,
ClientFingerprint: option.ClientFingerprint,
}
}

View file

@ -1,24 +1,17 @@
package trojan
import (
"context"
"crypto/sha256"
"crypto/tls"
"encoding/binary"
"encoding/hex"
"errors"
"io"
"net"
"net/http"
"sync"
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/component/ca"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/socks5"
"github.com/metacubex/mihomo/transport/vmess"
)
const (
@ -27,8 +20,8 @@ const (
)
var (
defaultALPN = []string{"h2", "http/1.1"}
defaultWebsocketALPN = []string{"http/1.1"}
DefaultALPN = []string{"h2", "http/1.1"}
DefaultWebsocketALPN = []string{"http/1.1"}
crlf = []byte{'\r', '\n'}
)
@ -43,115 +36,11 @@ const (
KeyLength = 56
)
type Option struct {
Password string
ALPN []string
ServerName string
SkipCertVerify bool
Fingerprint string
ClientFingerprint string
Reality *tlsC.RealityConfig
}
type WebsocketOption struct {
Host string
Port string
Path string
Headers http.Header
V2rayHttpUpgrade bool
V2rayHttpUpgradeFastOpen bool
}
type Trojan struct {
option *Option
hexPassword [KeyLength]byte
}
func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error) {
alpn := defaultALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
}
tlsConfig := &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.ServerName,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
if err != nil {
return nil, err
}
if len(t.option.ClientFingerprint) != 0 {
if t.option.Reality == nil {
utlsConn, valid := vmess.GetUTLSConn(conn, t.option.ClientFingerprint, tlsConfig)
if valid {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err := utlsConn.HandshakeContext(ctx)
return utlsConn, err
}
} else {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
return tlsC.GetRealityConn(ctx, conn, t.option.ClientFingerprint, tlsConfig, t.option.Reality)
}
}
if t.option.Reality != nil {
return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint")
}
tlsConn := tls.Client(conn, tlsConfig)
// fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err = tlsConn.HandshakeContext(ctx)
return tlsConn, err
}
func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptions *WebsocketOption) (net.Conn, error) {
alpn := defaultWebsocketALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
}
tlsConfig := &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.ServerName,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
if err != nil {
return nil, err
}
return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{
Host: wsOptions.Host,
Port: wsOptions.Port,
Path: wsOptions.Path,
Headers: wsOptions.Headers,
V2rayHttpUpgrade: wsOptions.V2rayHttpUpgrade,
V2rayHttpUpgradeFastOpen: wsOptions.V2rayHttpUpgradeFastOpen,
TLS: true,
TLSConfig: tlsConfig,
ClientFingerprint: t.option.ClientFingerprint,
})
}
func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) error {
func WriteHeader(w io.Writer, hexPassword [KeyLength]byte, command Command, socks5Addr []byte) error {
buf := pool.GetBuffer()
defer pool.PutBuffer(buf)
buf.Write(t.hexPassword[:])
buf.Write(hexPassword[:])
buf.Write(crlf)
buf.WriteByte(command)
@ -162,12 +51,6 @@ func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) er
return err
}
func (t *Trojan) PacketConn(conn net.Conn) net.PacketConn {
return &PacketConn{
Conn: conn,
}
}
func writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {
buf := pool.GetBuffer()
defer pool.PutBuffer(buf)
@ -243,10 +126,6 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, int, error) {
return uAddr, length, total - length, nil
}
func New(option *Option) *Trojan {
return &Trojan{option, Key(option.Password)}
}
var _ N.EnhancePacketConn = (*PacketConn)(nil)
type PacketConn struct {