chore: shadowtls don't depend on trojan's code

This commit is contained in:
wwqgtxx 2023-01-11 10:19:30 +08:00
parent 0c9a23a53c
commit 0069513780
2 changed files with 58 additions and 31 deletions

View File

@ -2,6 +2,7 @@ package outbound
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -9,11 +10,11 @@ import (
"github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/shadowtls" "github.com/Dreamacro/clash/transport/shadowtls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs" obfs "github.com/Dreamacro/clash/transport/simple-obfs"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/clash/transport/trojan"
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
shadowsocks "github.com/metacubex/sing-shadowsocks" shadowsocks "github.com/metacubex/sing-shadowsocks"
@ -33,7 +34,7 @@ type ShadowSocks struct {
obfsOption *simpleObfsOption obfsOption *simpleObfsOption
v2rayOption *v2rayObfs.Option v2rayOption *v2rayObfs.Option
shadowTLSOption *shadowTLSOption shadowTLSOption *shadowTLSOption
tlsConnector *trojan.Trojan tlsConfig *tls.Config
} }
type ShadowSocksOption struct { type ShadowSocksOption struct {
@ -66,8 +67,10 @@ type v2rayObfsOption struct {
} }
type shadowTLSOption struct { type shadowTLSOption struct {
Password string `obfs:"password"` Password string `obfs:"password"`
Host string `obfs:"host"` Host string `obfs:"host"`
Fingerprint string `obfs:"fingerprint,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
} }
// StreamConn implements C.ProxyAdapter // StreamConn implements C.ProxyAdapter
@ -85,10 +88,7 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
} }
case shadowtls.Mode: case shadowtls.Mode:
if ss.tlsConnector == nil { c = shadowtls.NewShadowTLS(c, ss.shadowTLSOption.Password, ss.tlsConfig)
ss.tlsConnector = trojan.New(&trojan.Option{ServerName: ss.shadowTLSOption.Host, SkipCertVerify: false})
}
c = shadowtls.NewShadowTls(c, ss.shadowTLSOption.Password, ss.tlsConnector)
} }
if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { if metadata.NetWork == C.UDP && ss.option.UDPOverTCP {
return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443"))
@ -172,6 +172,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
var v2rayOption *v2rayObfs.Option var v2rayOption *v2rayObfs.Option
var obfsOption *simpleObfsOption var obfsOption *simpleObfsOption
var shadowTLSOpt *shadowTLSOption var shadowTLSOpt *shadowTLSOption
var tlsConfig *tls.Config
obfsMode := "" obfsMode := ""
decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
@ -213,6 +214,21 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil { if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil {
return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err) return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
} }
tlsConfig = &tls.Config{
NextProtos: shadowtls.DefaultALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: shadowTLSOpt.SkipCertVerify,
ServerName: shadowTLSOpt.Host,
}
if len(shadowTLSOpt.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil {
return nil, err
}
}
} }
return &ShadowSocks{ return &ShadowSocks{
@ -232,6 +248,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
v2rayOption: v2rayOption, v2rayOption: v2rayOption,
obfsOption: obfsOption, obfsOption: obfsOption,
shadowTLSOption: shadowTLSOpt, shadowTLSOption: shadowTLSOpt,
tlsConfig: tlsConfig,
}, nil }, nil
} }

View File

@ -1,8 +1,10 @@
package shadowtls package shadowtls
import ( import (
"context"
"crypto/hmac" "crypto/hmac"
"crypto/sha1" "crypto/sha1"
"crypto/tls"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"hash" "hash"
@ -10,7 +12,7 @@ import (
"net" "net"
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/transport/trojan" C "github.com/Dreamacro/clash/constant"
) )
const ( const (
@ -20,13 +22,17 @@ const (
tlsHeaderLen int = 5 tlsHeaderLen int = 5
) )
// TLSObfs is shadowsocks tls simple-obfs implementation var (
type ShadowTls struct { DefaultALPN = []string{"h2", "http/1.1"}
)
// ShadowTLS is shadow-tls implementation
type ShadowTLS struct {
net.Conn net.Conn
password []byte password []byte
remain int remain int
firstRequest bool firstRequest bool
tlsConnector *trojan.Trojan tlsConfig *tls.Config
} }
type HashedConn struct { type HashedConn struct {
@ -47,9 +53,9 @@ func (h HashedConn) Read(b []byte) (n int, err error) {
return return
} }
func (to *ShadowTls) read(b []byte) (int, error) { func (s *ShadowTLS) read(b []byte) (int, error) {
buf := pool.Get(tlsHeaderLen) buf := pool.Get(tlsHeaderLen)
_, err := io.ReadFull(to.Conn, buf) _, err := io.ReadFull(s.Conn, buf)
if err != nil { if err != nil {
return 0, fmt.Errorf("shadowtls read failed %w", err) return 0, fmt.Errorf("shadowtls read failed %w", err)
} }
@ -60,36 +66,36 @@ func (to *ShadowTls) read(b []byte) (int, error) {
pool.Put(buf) pool.Put(buf)
if length > len(b) { if length > len(b) {
n, err := to.Conn.Read(b) n, err := s.Conn.Read(b)
if err != nil { if err != nil {
return n, err return n, err
} }
to.remain = length - n s.remain = length - n
return n, nil return n, nil
} }
return io.ReadFull(to.Conn, b[:length]) return io.ReadFull(s.Conn, b[:length])
} }
func (to *ShadowTls) Read(b []byte) (int, error) { func (s *ShadowTLS) Read(b []byte) (int, error) {
if to.remain > 0 { if s.remain > 0 {
length := to.remain length := s.remain
if length > len(b) { if length > len(b) {
length = len(b) length = len(b)
} }
n, err := io.ReadFull(to.Conn, b[:length]) n, err := io.ReadFull(s.Conn, b[:length])
if err != nil { if err != nil {
return n, fmt.Errorf("shadowtls Read failed with %w", err) return n, fmt.Errorf("shadowtls Read failed with %w", err)
} }
to.remain -= n s.remain -= n
return n, nil return n, nil
} }
return to.read(b) return s.read(b)
} }
func (to *ShadowTls) Write(b []byte) (int, error) { func (s *ShadowTLS) Write(b []byte) (int, error) {
length := len(b) length := len(b)
for i := 0; i < length; i += chunkSize { for i := 0; i < length; i += chunkSize {
end := i + chunkSize end := i + chunkSize
@ -97,7 +103,7 @@ func (to *ShadowTls) Write(b []byte) (int, error) {
end = length end = length
} }
n, err := to.write(b[i:end]) n, err := s.write(b[i:end])
if err != nil { if err != nil {
return n, fmt.Errorf("shadowtls Write failed with %w, i=%d, end=%d, n=%d", err, i, end, n) return n, fmt.Errorf("shadowtls Write failed with %w, i=%d, end=%d, n=%d", err, i, end, n)
} }
@ -105,11 +111,15 @@ func (to *ShadowTls) Write(b []byte) (int, error) {
return length, nil return length, nil
} }
func (s *ShadowTls) write(b []byte) (int, error) { func (s *ShadowTLS) write(b []byte) (int, error) {
var hashVal []byte var hashVal []byte
if s.firstRequest { if s.firstRequest {
hashedConn := newHashedStream(s.Conn, s.password) hashedConn := newHashedStream(s.Conn, s.password)
if _, err := s.tlsConnector.StreamConn(hashedConn); err != nil { tlsConn := tls.Client(hashedConn, s.tlsConfig)
// fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
if err := tlsConn.HandshakeContext(ctx); err != nil {
return 0, fmt.Errorf("tls connect failed with %w", err) return 0, fmt.Errorf("tls connect failed with %w", err)
} }
hashVal = hashedConn.hasher.Sum(nil)[:hashLen] hashVal = hashedConn.hasher.Sum(nil)[:hashLen]
@ -131,12 +141,12 @@ func (s *ShadowTls) write(b []byte) (int, error) {
return len(b), nil return len(b), nil
} }
// NewShadowTls return a ShadowTls // NewShadowTLS return a ShadowTLS
func NewShadowTls(conn net.Conn, password string, tlsConnector *trojan.Trojan) net.Conn { func NewShadowTLS(conn net.Conn, password string, tlsConfig *tls.Config) net.Conn {
return &ShadowTls{ return &ShadowTLS{
Conn: conn, Conn: conn,
password: []byte(password), password: []byte(password),
firstRequest: true, firstRequest: true,
tlsConnector: tlsConnector, tlsConfig: tlsConfig,
} }
} }