chore: shadowtls don't depend on trojan's code
This commit is contained in:
parent
0c9a23a53c
commit
0069513780
@ -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 {
|
||||||
@ -68,6 +69,8 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user