2023-02-21 11:19:47 +00:00
|
|
|
package shadowtls
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"github.com/sagernet/sing-shadowtls"
|
2023-02-21 12:42:44 +00:00
|
|
|
sing_common "github.com/sagernet/sing/common"
|
|
|
|
utls "github.com/sagernet/utls"
|
2023-02-21 11:19:47 +00:00
|
|
|
"github.com/xtls/xray-core/common"
|
|
|
|
"github.com/xtls/xray-core/common/net"
|
|
|
|
"github.com/xtls/xray-core/common/session"
|
|
|
|
"github.com/xtls/xray-core/common/singbridge"
|
|
|
|
"github.com/xtls/xray-core/transport"
|
|
|
|
"github.com/xtls/xray-core/transport/internet"
|
2023-02-21 12:42:44 +00:00
|
|
|
internet_tls "github.com/xtls/xray-core/transport/internet/tls"
|
2023-02-21 11:19:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
|
|
return NewClient(ctx, config.(*ClientConfig))
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
type Outbound struct {
|
|
|
|
ctx context.Context
|
|
|
|
clientConfig shadowtls.ClientConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) {
|
|
|
|
return &Outbound{
|
|
|
|
ctx: ctx,
|
|
|
|
clientConfig: shadowtls.ClientConfig{
|
|
|
|
Version: int(config.Version),
|
|
|
|
Password: config.Password,
|
|
|
|
Server: singbridge.ToSocksaddr(net.Destination{
|
|
|
|
Address: config.Address.AsAddress(),
|
|
|
|
Port: net.Port(config.Port),
|
|
|
|
}),
|
|
|
|
Logger: singbridge.NewLogger(newError),
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
|
|
|
var inboundConn net.Conn
|
|
|
|
inbound := session.InboundFromContext(ctx)
|
|
|
|
if inbound != nil {
|
|
|
|
inboundConn = inbound.Conn
|
|
|
|
}
|
|
|
|
|
|
|
|
outbound := session.OutboundFromContext(ctx)
|
|
|
|
if outbound == nil || !outbound.Target.IsValid() {
|
|
|
|
return newError("target not specified")
|
|
|
|
}
|
|
|
|
destination := outbound.Target
|
|
|
|
|
|
|
|
if destination.Network != net.Network_TCP {
|
|
|
|
return newError("only TCP is supported")
|
|
|
|
}
|
|
|
|
|
|
|
|
newError("tunneling request to ", destination, " via ", o.clientConfig.Server).WriteToLog(session.ExportIDToError(ctx))
|
|
|
|
|
|
|
|
var client *shadowtls.Client
|
|
|
|
clientConfig := o.clientConfig
|
2023-02-21 12:42:44 +00:00
|
|
|
clientConfig.Dialer = singbridge.NewTLSDialer(dialer, func(conn net.Conn, xrayConfig *internet_tls.Config, config *tls.Config) net.Conn {
|
2023-02-21 14:55:37 +00:00
|
|
|
if fingerprint := GetFingerprint(xrayConfig.Fingerprint); fingerprint != nil {
|
|
|
|
client.SetHandshakeFunc(uTLSHandshakeFunc(config, fingerprint))
|
2023-02-21 12:42:44 +00:00
|
|
|
} else {
|
|
|
|
client.SetHandshakeFunc(shadowtls.DefaultTLSHandshakeFunc(clientConfig.Password, config))
|
|
|
|
}
|
|
|
|
return conn
|
|
|
|
})
|
2023-02-21 11:19:47 +00:00
|
|
|
var err error
|
|
|
|
client, err = shadowtls.NewClient(clientConfig)
|
|
|
|
if err != nil {
|
|
|
|
return newError("failed to create client").Base(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := client.DialContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return newError("failed to connect to server").Base(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return singbridge.CopyConn(ctx, inboundConn, link, conn)
|
|
|
|
}
|
2023-02-21 12:42:44 +00:00
|
|
|
|
2023-02-21 14:55:37 +00:00
|
|
|
func uTLSHandshakeFunc(config *tls.Config, clientHelloID *utls.ClientHelloID) shadowtls.TLSHandshakeFunc {
|
2023-02-21 12:42:44 +00:00
|
|
|
return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
|
|
|
tlsConfig := &utls.Config{
|
|
|
|
Rand: config.Rand,
|
|
|
|
Time: config.Time,
|
|
|
|
VerifyPeerCertificate: config.VerifyPeerCertificate,
|
|
|
|
RootCAs: config.RootCAs,
|
|
|
|
NextProtos: config.NextProtos,
|
|
|
|
ServerName: config.ServerName,
|
|
|
|
InsecureSkipVerify: config.InsecureSkipVerify,
|
|
|
|
CipherSuites: config.CipherSuites,
|
|
|
|
MinVersion: config.MinVersion,
|
|
|
|
MaxVersion: config.MaxVersion,
|
|
|
|
CurvePreferences: sing_common.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {
|
|
|
|
return utls.CurveID(it)
|
|
|
|
}),
|
|
|
|
SessionTicketsDisabled: config.SessionTicketsDisabled,
|
|
|
|
Renegotiation: utls.RenegotiationSupport(config.Renegotiation),
|
|
|
|
SessionIDGenerator: sessionIDGenerator,
|
|
|
|
}
|
2023-02-21 14:55:37 +00:00
|
|
|
tlsConn := utls.UClient(conn, tlsConfig, *clientHelloID)
|
2023-02-21 12:42:44 +00:00
|
|
|
return tlsConn.HandshakeContext(ctx)
|
|
|
|
}
|
|
|
|
}
|