diff --git a/go.mod b/go.mod index a7719a3cd..e6afb396d 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 6ad100a31..a9462d621 100644 --- a/go.sum +++ b/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= diff --git a/main/distro/all/all.go b/main/distro/all/all.go index fc6261250..efe8042cb 100644 --- a/main/distro/all/all.go +++ b/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" diff --git a/transport/internet/tls/utls/config.pb.go b/transport/internet/tls/utls/config.pb.go new file mode 100644 index 000000000..a5019991c --- /dev/null +++ b/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 +} diff --git a/transport/internet/tls/utls/config.proto b/transport/internet/tls/utls/config.proto new file mode 100644 index 000000000..33557a4b8 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/transport/internet/tls/utls/errors.generated.go b/transport/internet/tls/utls/errors.generated.go new file mode 100644 index 000000000..4300512fb --- /dev/null +++ b/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{}) +} diff --git a/transport/internet/tls/utls/nameMapper.go b/transport/internet/tls/utls/nameMapper.go new file mode 100644 index 000000000..a50426199 --- /dev/null +++ b/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 +} diff --git a/transport/internet/tls/utls/utls.go b/transport/internet/tls/utls/utls.go new file mode 100644 index 000000000..d6071a243 --- /dev/null +++ b/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)) + })) +} diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 9244938f7..5357971bb 100644 --- a/transport/internet/websocket/dialer.go +++ b/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.