Add uTLS support for Security Engine

This commit is contained in:
Shelikhoo 2022-12-16 20:50:53 +00:00 committed by Xiaokang Wang (Shelikhoo)
parent 4a887e3b77
commit e4188c8604
9 changed files with 378 additions and 7 deletions

7
go.mod
View File

@ -19,6 +19,7 @@ require (
github.com/mustafaturan/bus v1.0.2
github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.6.2
github.com/refraction-networking/utls v1.2.0
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
github.com/stretchr/testify v1.8.1
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08
@ -27,8 +28,8 @@ require (
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432
go.starlark.net v0.0.0-20220817180228-f738f5508c12
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
golang.org/x/crypto v0.1.0
golang.org/x/net v0.1.0
golang.org/x/sync v0.0.0-20220907140024-f12130a52804
golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06
google.golang.org/grpc v1.51.0
@ -40,6 +41,7 @@ require (
require (
github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
@ -48,6 +50,7 @@ require (
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/klauspost/cpuid v1.2.3 // indirect
github.com/klauspost/reedsolomon v1.9.3 // indirect
github.com/leodido/go-urn v1.2.1 // indirect

14
go.sum
View File

@ -23,6 +23,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@ -173,6 +175,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY=
@ -263,6 +267,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo=
github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -348,8 +354,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -395,8 +401,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@ -61,6 +61,7 @@ import (
_ "github.com/v2fly/v2ray-core/v5/transport/internet/quic"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/tcp"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/tls"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/tls/utls"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/udp"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/websocket"

View File

@ -0,0 +1,180 @@
package utls
import (
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TlsConfig *tls.Config `protobuf:"bytes,1,opt,name=tls_config,json=tlsConfig,proto3" json:"tls_config,omitempty"`
Imitate string `protobuf:"bytes,2,opt,name=imitate,proto3" json:"imitate,omitempty"`
NoSNI bool `protobuf:"varint,3,opt,name=noSNI,proto3" json:"noSNI,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_tls_utls_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_tls_utls_config_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_transport_internet_tls_utls_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetTlsConfig() *tls.Config {
if x != nil {
return x.TlsConfig
}
return nil
}
func (x *Config) GetImitate() string {
if x != nil {
return x.Imitate
}
return ""
}
func (x *Config) GetNoSNI() bool {
if x != nil {
return x.NoSNI
}
return false
}
var File_transport_internet_tls_utls_config_proto protoreflect.FileDescriptor
var file_transport_internet_tls_utls_config_proto_rawDesc = []byte{
0x0a, 0x28, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x75, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x26, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x75, 0x74,
0x6c, 0x73, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x23, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x01, 0x0a, 0x06, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x48, 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
0x0a, 0x07, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x53, 0x4e,
0x49, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x6e, 0x6f, 0x53, 0x4e, 0x49, 0x3a, 0x18,
0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x82, 0xb5,
0x18, 0x06, 0x12, 0x04, 0x75, 0x74, 0x6c, 0x73, 0x42, 0x93, 0x01, 0x0a, 0x2a, 0x63, 0x6f, 0x6d,
0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
0x6c, 0x73, 0x2e, 0x75, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73,
0x2f, 0x75, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x26, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f,
0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x2e, 0x55, 0x54, 0x6c, 0x73, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_tls_utls_config_proto_rawDescOnce sync.Once
file_transport_internet_tls_utls_config_proto_rawDescData = file_transport_internet_tls_utls_config_proto_rawDesc
)
func file_transport_internet_tls_utls_config_proto_rawDescGZIP() []byte {
file_transport_internet_tls_utls_config_proto_rawDescOnce.Do(func() {
file_transport_internet_tls_utls_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_tls_utls_config_proto_rawDescData)
})
return file_transport_internet_tls_utls_config_proto_rawDescData
}
var file_transport_internet_tls_utls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_tls_utls_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: v2ray.core.transport.internet.tls.utls.Config
(*tls.Config)(nil), // 1: v2ray.core.transport.internet.tls.Config
}
var file_transport_internet_tls_utls_config_proto_depIdxs = []int32{
1, // 0: v2ray.core.transport.internet.tls.utls.Config.tls_config:type_name -> v2ray.core.transport.internet.tls.Config
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_transport_internet_tls_utls_config_proto_init() }
func file_transport_internet_tls_utls_config_proto_init() {
if File_transport_internet_tls_utls_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_tls_utls_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_tls_utls_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_tls_utls_config_proto_goTypes,
DependencyIndexes: file_transport_internet_tls_utls_config_proto_depIdxs,
MessageInfos: file_transport_internet_tls_utls_config_proto_msgTypes,
}.Build()
File_transport_internet_tls_utls_config_proto = out.File
file_transport_internet_tls_utls_config_proto_rawDesc = nil
file_transport_internet_tls_utls_config_proto_goTypes = nil
file_transport_internet_tls_utls_config_proto_depIdxs = nil
}

View File

@ -0,0 +1,19 @@
syntax = "proto3";
package v2ray.core.transport.internet.tls.utls;
option csharp_namespace = "V2Ray.Core.Transport.Internet.Tls.UTls";
option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tls/utls";
option java_package = "com.v2ray.core.transport.internet.tls.utls";
option java_multiple_files = true;
import "common/protoext/extensions.proto";
import "transport/internet/tls/config.proto";
message Config {
option (v2ray.core.common.protoext.message_opt).type = "security";
option (v2ray.core.common.protoext.message_opt).short_name = "utls";
v2ray.core.transport.internet.tls.Config tls_config = 1;
string imitate = 2;
bool noSNI = 3;
}

View File

@ -0,0 +1,9 @@
package utls
import "github.com/v2fly/v2ray-core/v5/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

View File

@ -0,0 +1,51 @@
package utls
import utls "github.com/refraction-networking/utls"
var clientHelloIDMap = map[string]*utls.ClientHelloID{
"randomized": &utls.HelloRandomized,
"randomizedalpn": &utls.HelloRandomizedALPN,
"randomizednoalpn": &utls.HelloRandomizedNoALPN,
"firefox_auto": &utls.HelloFirefox_Auto,
"firefox_55": &utls.HelloFirefox_55,
"firefox_56": &utls.HelloFirefox_56,
"firefox_63": &utls.HelloFirefox_63,
"firefox_65": &utls.HelloFirefox_65,
"firefox_99": &utls.HelloFirefox_99,
"firefox_102": &utls.HelloFirefox_102,
"firefox_105": &utls.HelloFirefox_105,
"chrome_auto": &utls.HelloChrome_Auto,
"chrome_58": &utls.HelloChrome_58,
"chrome_62": &utls.HelloChrome_62,
"chrome_70": &utls.HelloChrome_70,
"chrome_72": &utls.HelloChrome_72,
"chrome_83": &utls.HelloChrome_83,
"chrome_87": &utls.HelloChrome_87,
"chrome_96": &utls.HelloChrome_96,
"chrome_100": &utls.HelloChrome_100,
"chrome_102": &utls.HelloChrome_102,
"ios_auto": &utls.HelloIOS_Auto,
"ios_11_1": &utls.HelloIOS_11_1,
"ios_12_1": &utls.HelloIOS_12_1,
"ios_13": &utls.HelloIOS_13,
"ios_14": &utls.HelloIOS_14,
"android_11_okhttp": &utls.HelloAndroid_11_OkHttp,
"edge_auto": &utls.HelloEdge_Auto,
"edge_85": &utls.HelloEdge_85,
"edge_106": &utls.HelloEdge_106,
"safari_auto": &utls.HelloSafari_Auto,
"safari_16_0": &utls.HelloSafari_16_0,
"360_auto": &utls.Hello360_Auto,
"360_7_5": &utls.Hello360_7_5,
"360_11_0": &utls.Hello360_11_0,
"qq_auto": &utls.HelloQQ_Auto,
"qq_11_1": &utls.HelloQQ_11_1,
}
func nameToUTLSPreset(name string) (*utls.ClientHelloID, error) {
preset, ok := clientHelloIDMap[name]
if !ok {
return nil, newError("unknown preset name")
}
return preset, nil
}

View File

@ -0,0 +1,102 @@
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:
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 _, v := range utlsClientConn.Extensions {
if aplnExtension, ok := v.(*utls.ALPNExtension); ok {
aplnExtension.AlpnProtocols = tlsConfig.NextProtos
}
}
}
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 utlsClientConn, 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))
}))
}

View File

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/base64"
"github.com/v2fly/v2ray-core/v5/transport/internet/security"
"io"
gonet "net"
"net/http"
@ -18,6 +17,7 @@ import (
"github.com/v2fly/v2ray-core/v5/common/session"
"github.com/v2fly/v2ray-core/v5/features/extension"
"github.com/v2fly/v2ray-core/v5/transport/internet"
"github.com/v2fly/v2ray-core/v5/transport/internet/security"
)
// Dial dials a WebSocket connection to the given destination.