2023-02-21 21:58:37 +08:00
|
|
|
package sing_shadowtls
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"net"
|
|
|
|
|
2023-11-03 21:01:45 +08:00
|
|
|
"github.com/metacubex/mihomo/component/ca"
|
|
|
|
tlsC "github.com/metacubex/mihomo/component/tls"
|
|
|
|
"github.com/metacubex/mihomo/log"
|
2023-02-21 21:58:37 +08:00
|
|
|
|
2025-04-12 20:27:30 +08:00
|
|
|
"github.com/metacubex/sing-shadowtls"
|
2025-04-22 23:44:55 +08:00
|
|
|
"golang.org/x/exp/slices"
|
2023-02-21 21:58:37 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
Mode string = "shadow-tls"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
DefaultALPN = []string{"h2", "http/1.1"}
|
2025-04-22 23:44:55 +08:00
|
|
|
WsALPN = []string{"http/1.1"}
|
2023-02-21 21:58:37 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type ShadowTLSOption struct {
|
|
|
|
Password string
|
|
|
|
Host string
|
|
|
|
Fingerprint string
|
|
|
|
ClientFingerprint string
|
|
|
|
SkipCertVerify bool
|
|
|
|
Version int
|
2025-04-22 20:49:54 +08:00
|
|
|
ALPN []string
|
2023-02-21 21:58:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
|
|
|
|
tlsConfig := &tls.Config{
|
2025-04-22 20:49:54 +08:00
|
|
|
NextProtos: option.ALPN,
|
2023-02-21 21:58:37 +08:00
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
InsecureSkipVerify: option.SkipCertVerify,
|
|
|
|
ServerName: option.Host,
|
|
|
|
}
|
2025-04-22 20:09:24 +08:00
|
|
|
if option.Version == 1 {
|
|
|
|
tlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2
|
|
|
|
}
|
2023-02-21 21:58:37 +08:00
|
|
|
|
|
|
|
var err error
|
2023-09-22 14:45:34 +08:00
|
|
|
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-02-21 21:58:37 +08:00
|
|
|
}
|
|
|
|
|
2025-04-19 02:04:09 +08:00
|
|
|
tlsHandshake := uTLSHandshakeFunc(tlsConfig, option.ClientFingerprint)
|
2023-02-21 21:58:37 +08:00
|
|
|
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
|
|
|
|
Version: option.Version,
|
|
|
|
Password: option.Password,
|
|
|
|
TLSHandshake: tlsHandshake,
|
|
|
|
Logger: log.SingLogger,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return client.DialContextConn(ctx, conn)
|
|
|
|
}
|
|
|
|
|
2025-04-19 02:04:09 +08:00
|
|
|
func uTLSHandshakeFunc(config *tls.Config, clientFingerprint string) shadowtls.TLSHandshakeFunc {
|
2023-02-21 21:58:37 +08:00
|
|
|
return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
2025-04-21 12:07:33 +08:00
|
|
|
tlsConfig := tlsC.UConfig(config)
|
|
|
|
tlsConfig.SessionIDGenerator = sessionIDGenerator
|
2025-04-22 23:44:55 +08:00
|
|
|
if config.MaxVersion == tls.VersionTLS12 { // for ShadowTLS v1
|
2025-04-29 21:15:48 +08:00
|
|
|
tlsConn := tlsC.Client(conn, tlsConfig)
|
|
|
|
return tlsConn.HandshakeContext(ctx)
|
2025-04-22 23:44:55 +08:00
|
|
|
}
|
2025-04-29 21:15:48 +08:00
|
|
|
if clientFingerprint, ok := tlsC.GetFingerprint(clientFingerprint); ok {
|
|
|
|
tlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint)
|
|
|
|
if slices.Equal(tlsConfig.NextProtos, WsALPN) {
|
|
|
|
err := tlsC.BuildWebsocketHandshakeState(tlsConn)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2025-04-22 23:44:55 +08:00
|
|
|
}
|
2025-04-19 02:04:09 +08:00
|
|
|
}
|
2025-04-29 21:15:48 +08:00
|
|
|
return tlsConn.HandshakeContext(ctx)
|
2025-04-12 20:27:30 +08:00
|
|
|
}
|
2025-04-29 21:15:48 +08:00
|
|
|
tlsConn := tlsC.Client(conn, tlsConfig)
|
2023-02-21 21:58:37 +08:00
|
|
|
return tlsConn.HandshakeContext(ctx)
|
|
|
|
}
|
|
|
|
}
|