Browse Source

Add uTLS support for Security Engine

Shelikhoo 2 years ago
parent
commit
6d4ee5acac

+ 5 - 2
go.mod

@@ -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

+ 10 - 4
go.sum

@@ -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=

+ 1 - 0
main/distro/all/all.go

@@ -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"
 

+ 180 - 0
transport/internet/tls/utls/config.pb.go

@@ -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
+}

+ 19 - 0
transport/internet/tls/utls/config.proto

@@ -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;
+}

+ 9 - 0
transport/internet/tls/utls/errors.generated.go

@@ -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{})
+}

+ 51 - 0
transport/internet/tls/utls/nameMapper.go

@@ -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
+}

+ 102 - 0
transport/internet/tls/utls/utls.go

@@ -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))
+	}))
+}

+ 1 - 1
transport/internet/websocket/dialer.go

@@ -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.