1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-07-16 02:04:35 -04:00
v2fly/transport/internet/tls/utls/utls.go
2023-05-30 17:48:55 +01:00

123 lines
3.5 KiB
Go

package utls
import (
"context"
systls "crypto/tls"
utls "github.com/refraction-networking/utls"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet/security"
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
)
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
func NewUTLSSecurityEngineFromConfig(config *Config) (security.Engine, error) {
if config.TlsConfig == nil {
return nil, newError("mandatory field tls_config is not specified")
}
return &Engine{config: config}, nil
}
type Engine struct {
config *Config
}
func (e Engine) Client(conn net.Conn, opts ...security.Option) (security.Conn, error) {
var options []tls.Option
for _, v := range opts {
switch s := v.(type) {
case security.OptionWithALPN:
if e.config.ForceAlpn == ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY {
options = append(options, tls.WithNextProto(s.ALPNs...))
}
case security.OptionWithDestination:
options = append(options, tls.WithDestination(s.Dest))
default:
return nil, newError("unknown option")
}
}
tlsConfig := e.config.TlsConfig.GetTLSConfig(options...)
utlsConfig, err := uTLSConfigFromTLSConfig(tlsConfig)
if err != nil {
return nil, newError("unable to generate utls config from tls config").Base(err)
}
preset, err := nameToUTLSPreset(e.config.Imitate)
if err != nil {
return nil, newError("unable to get utls preset from name").Base(err)
}
utlsClientConn := utls.UClient(conn, utlsConfig, *preset)
if e.config.NoSNI {
err = utlsClientConn.RemoveSNIExtension()
if err != nil {
return nil, newError("unable to remove server name indication from utls client hello").Base(err)
}
}
err = utlsClientConn.BuildHandshakeState()
if err != nil {
return nil, newError("unable to build utls handshake state").Base(err)
}
// ALPN is necessary for protocols like websocket to work. The uTLS setting may be overwritten on call into
// BuildHandshakeState, so we need to check the original tls settings.
if tlsConfig.NextProtos != nil {
for n, v := range utlsClientConn.Extensions {
if aplnExtension, ok := v.(*utls.ALPNExtension); ok {
if e.config.ForceAlpn == ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY {
aplnExtension.AlpnProtocols = tlsConfig.NextProtos
break
}
if e.config.ForceAlpn == ForcedALPN_NO_ALPN {
utlsClientConn.Extensions = append(utlsClientConn.Extensions[:n], utlsClientConn.Extensions[n+1:]...)
break
}
}
}
}
err = utlsClientConn.BuildHandshakeState()
if err != nil {
return nil, newError("unable to build utls handshake state after modification").Base(err)
}
err = utlsClientConn.Handshake()
if err != nil {
return nil, newError("unable to finish utls handshake").Base(err)
}
return uTLSClientConnection{utlsClientConn}, nil
}
type uTLSClientConnection struct {
*utls.UConn
}
func (u uTLSClientConnection) GetConnectionApplicationProtocol() (string, error) {
if err := u.Handshake(); err != nil {
return "", err
}
return u.ConnectionState().NegotiatedProtocol, nil
}
func uTLSConfigFromTLSConfig(config *systls.Config) (*utls.Config, error) { // nolint: unparam
uconfig := &utls.Config{
Rand: config.Rand,
Time: config.Time,
RootCAs: config.RootCAs,
NextProtos: config.NextProtos,
ServerName: config.ServerName,
}
return uconfig, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewUTLSSecurityEngineFromConfig(config.(*Config))
}))
}