1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-09-19 10:26:10 -04:00

Add (Experimental) Meyka Building Blocks to request Transport (#3120)

* add packetconn assembler

* let kcp use environment dependency injection

* Add destination override to simplified setting

* add dtls dialer

* add dtls listener

* add dtls to default

* fix bugs

* add debug options to freedom outbound

* fix kcp test failure for transport environment
This commit is contained in:
Xiaokang Wang (Shelikhoo) 2024-08-22 04:05:05 +01:00 committed by GitHub
parent b1079aae71
commit 7db39fb566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 2219 additions and 128 deletions

3
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/go-chi/chi/v5 v5.0.12
github.com/go-chi/render v1.0.3
github.com/go-playground/validator/v10 v10.20.0
github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.6.0
@ -19,6 +20,7 @@ require (
github.com/miekg/dns v1.1.59
github.com/mustafaturan/bus v1.0.2
github.com/pelletier/go-toml v1.9.5
github.com/pion/dtls/v2 v2.2.7
github.com/pion/transport/v2 v2.2.5
github.com/pires/go-proxyproto v0.7.0
github.com/quic-go/quic-go v0.43.0
@ -67,7 +69,6 @@ require (
github.com/mustafaturan/monoton v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/sctp v1.8.7 // indirect

1
go.sum
View File

@ -92,6 +92,7 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 h1:ZHJ7+IGpuOXtVf6Zk/a3WuHQgkC+vXwaqfUBDFwahtI=
github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@ -74,8 +74,12 @@ import (
_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/meek"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/dtls"
_ "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade"
// Transport headers

View File

@ -16,6 +16,55 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ProtocolReplacement int32
const (
ProtocolReplacement_IDENTITY ProtocolReplacement = 0
ProtocolReplacement_FORCE_TCP ProtocolReplacement = 1
ProtocolReplacement_FORCE_UDP ProtocolReplacement = 2
)
// Enum value maps for ProtocolReplacement.
var (
ProtocolReplacement_name = map[int32]string{
0: "IDENTITY",
1: "FORCE_TCP",
2: "FORCE_UDP",
}
ProtocolReplacement_value = map[string]int32{
"IDENTITY": 0,
"FORCE_TCP": 1,
"FORCE_UDP": 2,
}
)
func (x ProtocolReplacement) Enum() *ProtocolReplacement {
p := new(ProtocolReplacement)
*p = x
return p
}
func (x ProtocolReplacement) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ProtocolReplacement) Descriptor() protoreflect.EnumDescriptor {
return file_proxy_freedom_config_proto_enumTypes[0].Descriptor()
}
func (ProtocolReplacement) Type() protoreflect.EnumType {
return &file_proxy_freedom_config_proto_enumTypes[0]
}
func (x ProtocolReplacement) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ProtocolReplacement.Descriptor instead.
func (ProtocolReplacement) EnumDescriptor() ([]byte, []int) {
return file_proxy_freedom_config_proto_rawDescGZIP(), []int{0}
}
type Config_DomainStrategy int32
const (
@ -52,11 +101,11 @@ func (x Config_DomainStrategy) String() string {
}
func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_proxy_freedom_config_proto_enumTypes[0].Descriptor()
return file_proxy_freedom_config_proto_enumTypes[1].Descriptor()
}
func (Config_DomainStrategy) Type() protoreflect.EnumType {
return &file_proxy_freedom_config_proto_enumTypes[0]
return &file_proxy_freedom_config_proto_enumTypes[1]
}
func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
@ -125,6 +174,7 @@ type Config struct {
Timeout uint32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"`
DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
ProtocolReplacement ProtocolReplacement `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"`
}
func (x *Config) Reset() {
@ -188,10 +238,20 @@ func (x *Config) GetUserLevel() uint32 {
return 0
}
func (x *Config) GetProtocolReplacement() ProtocolReplacement {
if x != nil {
return x.ProtocolReplacement
}
return ProtocolReplacement_IDENTITY
}
type SimplifiedConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
ProtocolReplacement ProtocolReplacement `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"`
}
func (x *SimplifiedConfig) Reset() {
@ -226,6 +286,20 @@ func (*SimplifiedConfig) Descriptor() ([]byte, []int) {
return file_proxy_freedom_config_proto_rawDescGZIP(), []int{2}
}
func (x *SimplifiedConfig) GetDestinationOverride() *DestinationOverride {
if x != nil {
return x.DestinationOverride
}
return nil
}
func (x *SimplifiedConfig) GetProtocolReplacement() ProtocolReplacement {
if x != nil {
return x.ProtocolReplacement
}
return ProtocolReplacement_IDENTITY
}
var File_proxy_freedom_config_proto protoreflect.FileDescriptor
var file_proxy_freedom_config_proto_rawDesc = []byte{
@ -242,7 +316,7 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xc4, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xa6, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x58, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61,
0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x76, 0x32, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72,
@ -258,21 +332,44 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75,
0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x41, 0x0a, 0x0e, 0x44, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05,
0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x22, 0x2b, 0x0a,
0x10, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x12, 0x07, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x42, 0x69, 0x0a, 0x1c, 0x63, 0x6f,
0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x2c, 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, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x18, 0x56, 0x32, 0x52,
0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72,
0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x60, 0x0a, 0x14, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65,
0x64, 0x6f, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70, 0x6c,
0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
0x6c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x41, 0x0a, 0x0e,
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09,
0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45,
0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x22,
0xef, 0x01, 0x0a, 0x10, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x60, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65,
0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
0x65, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76,
0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x60, 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70,
0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08,
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x07, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f,
0x6d, 0x2a, 0x41, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70,
0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x44, 0x45, 0x4e,
0x54, 0x49, 0x54, 0x59, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f,
0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x55,
0x44, 0x50, 0x10, 0x02, 0x42, 0x69, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65,
0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x2c, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65,
0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x18, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -287,24 +384,28 @@ func file_proxy_freedom_config_proto_rawDescGZIP() []byte {
return file_proxy_freedom_config_proto_rawDescData
}
var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_proxy_freedom_config_proto_goTypes = []interface{}{
(Config_DomainStrategy)(0), // 0: v2ray.core.proxy.freedom.Config.DomainStrategy
(*DestinationOverride)(nil), // 1: v2ray.core.proxy.freedom.DestinationOverride
(*Config)(nil), // 2: v2ray.core.proxy.freedom.Config
(*SimplifiedConfig)(nil), // 3: v2ray.core.proxy.freedom.SimplifiedConfig
(*protocol.ServerEndpoint)(nil), // 4: v2ray.core.common.protocol.ServerEndpoint
var file_proxy_freedom_config_proto_goTypes = []any{
(ProtocolReplacement)(0), // 0: v2ray.core.proxy.freedom.ProtocolReplacement
(Config_DomainStrategy)(0), // 1: v2ray.core.proxy.freedom.Config.DomainStrategy
(*DestinationOverride)(nil), // 2: v2ray.core.proxy.freedom.DestinationOverride
(*Config)(nil), // 3: v2ray.core.proxy.freedom.Config
(*SimplifiedConfig)(nil), // 4: v2ray.core.proxy.freedom.SimplifiedConfig
(*protocol.ServerEndpoint)(nil), // 5: v2ray.core.common.protocol.ServerEndpoint
}
var file_proxy_freedom_config_proto_depIdxs = []int32{
4, // 0: v2ray.core.proxy.freedom.DestinationOverride.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
0, // 1: v2ray.core.proxy.freedom.Config.domain_strategy:type_name -> v2ray.core.proxy.freedom.Config.DomainStrategy
1, // 2: v2ray.core.proxy.freedom.Config.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
5, // 0: v2ray.core.proxy.freedom.DestinationOverride.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
1, // 1: v2ray.core.proxy.freedom.Config.domain_strategy:type_name -> v2ray.core.proxy.freedom.Config.DomainStrategy
2, // 2: v2ray.core.proxy.freedom.Config.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
0, // 3: v2ray.core.proxy.freedom.Config.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement
2, // 4: v2ray.core.proxy.freedom.SimplifiedConfig.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
0, // 5: v2ray.core.proxy.freedom.SimplifiedConfig.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_proxy_freedom_config_proto_init() }
@ -313,7 +414,7 @@ func file_proxy_freedom_config_proto_init() {
return
}
if !protoimpl.UnsafeEnabled {
file_proxy_freedom_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_proxy_freedom_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*DestinationOverride); i {
case 0:
return &v.state
@ -325,7 +426,7 @@ func file_proxy_freedom_config_proto_init() {
return nil
}
}
file_proxy_freedom_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
file_proxy_freedom_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*Config); i {
case 0:
return &v.state
@ -337,7 +438,7 @@ func file_proxy_freedom_config_proto_init() {
return nil
}
}
file_proxy_freedom_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
file_proxy_freedom_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
switch v := v.(*SimplifiedConfig); i {
case 0:
return &v.state
@ -355,7 +456,7 @@ func file_proxy_freedom_config_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_freedom_config_proto_rawDesc,
NumEnums: 1,
NumEnums: 2,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,

View File

@ -13,6 +13,12 @@ message DestinationOverride {
v2ray.core.common.protocol.ServerEndpoint server = 1;
}
enum ProtocolReplacement {
IDENTITY = 0;
FORCE_TCP = 1;
FORCE_UDP = 2;
}
message Config {
enum DomainStrategy {
AS_IS = 0;
@ -24,9 +30,13 @@ message Config {
uint32 timeout = 2 [deprecated = true];
DestinationOverride destination_override = 3;
uint32 user_level = 4;
ProtocolReplacement protocol_replacement = 5;
}
message SimplifiedConfig {
option (v2ray.core.common.protoext.message_opt).type = "outbound";
option (v2ray.core.common.protoext.message_opt).short_name = "freedom";
DestinationOverride destination_override = 3;
ProtocolReplacement protocol_replacement = 5;
}

View File

@ -35,7 +35,10 @@ func init() {
common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
simplifiedServer := config.(*SimplifiedConfig)
_ = simplifiedServer
fullConfig := &Config{}
fullConfig := &Config{
DestinationOverride: simplifiedServer.DestinationOverride,
ProtocolReplacement: simplifiedServer.ProtocolReplacement,
}
return common.CreateObject(ctx, fullConfig)
}))
}
@ -104,6 +107,14 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
destination.Port = net.Port(server.Port)
}
}
if h.config.ProtocolReplacement != ProtocolReplacement_IDENTITY {
if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_TCP {
destination.Network = net.Network_TCP
}
if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_UDP {
destination.Network = net.Network_UDP
}
}
if h.config.useIP() {
outbound.Resolver = func(ctx context.Context, domain string) net.Address {
return h.resolveIP(ctx, domain, dialer.Address())
@ -153,7 +164,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
var reader buf.Reader
if destination.Network == net.Network_TCP {
if destination.Network == net.Network_TCP && h.config.ProtocolReplacement == ProtocolReplacement_IDENTITY {
reader = buf.NewReader(conn)
} else {
reader = buf.NewPacketReader(conn)

View File

@ -0,0 +1,236 @@
package dtls
import (
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
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 DTLSMode int32
const (
DTLSMode_INVALID DTLSMode = 0
DTLSMode_PSK DTLSMode = 1
)
// Enum value maps for DTLSMode.
var (
DTLSMode_name = map[int32]string{
0: "INVALID",
1: "PSK",
}
DTLSMode_value = map[string]int32{
"INVALID": 0,
"PSK": 1,
}
)
func (x DTLSMode) Enum() *DTLSMode {
p := new(DTLSMode)
*p = x
return p
}
func (x DTLSMode) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DTLSMode) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_dtls_config_proto_enumTypes[0].Descriptor()
}
func (DTLSMode) Type() protoreflect.EnumType {
return &file_transport_internet_dtls_config_proto_enumTypes[0]
}
func (x DTLSMode) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DTLSMode.Descriptor instead.
func (DTLSMode) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_dtls_config_proto_rawDescGZIP(), []int{0}
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Mode DTLSMode `protobuf:"varint,1,opt,name=mode,proto3,enum=v2ray.core.transport.internet.dtls.DTLSMode" json:"mode,omitempty"`
Psk []byte `protobuf:"bytes,2,opt,name=psk,proto3" json:"psk,omitempty"`
Mtu uint32 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"`
ReplayProtectionWindow uint32 `protobuf:"varint,4,opt,name=replay_protection_window,json=replayProtectionWindow,proto3" json:"replay_protection_window,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_dtls_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_dtls_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_dtls_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetMode() DTLSMode {
if x != nil {
return x.Mode
}
return DTLSMode_INVALID
}
func (x *Config) GetPsk() []byte {
if x != nil {
return x.Psk
}
return nil
}
func (x *Config) GetMtu() uint32 {
if x != nil {
return x.Mtu
}
return 0
}
func (x *Config) GetReplayProtectionWindow() uint32 {
if x != nil {
return x.ReplayProtectionWindow
}
return 0
}
var File_transport_internet_dtls_config_proto protoreflect.FileDescriptor
var file_transport_internet_dtls_config_proto_rawDesc = []byte{
0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x22, 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, 0x64, 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, 0x22, 0xbf, 0x01, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 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, 0x64, 0x74, 0x6c, 0x73, 0x2e, 0x44, 0x54, 0x4c, 0x53, 0x4d,
0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x73, 0x6b,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x70, 0x73, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x6d,
0x74, 0x75, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x38, 0x0a,
0x18, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x3a, 0x15, 0x82, 0xb5, 0x18, 0x11, 0x0a, 0x09, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x04, 0x64, 0x74, 0x6c, 0x73, 0x2a, 0x20,
0x0a, 0x08, 0x44, 0x54, 0x4c, 0x53, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e,
0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x53, 0x4b, 0x10, 0x01,
0x42, 0x87, 0x01, 0x0a, 0x26, 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, 0x64, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x36, 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, 0x64, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x22, 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, 0x44, 0x74, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_transport_internet_dtls_config_proto_rawDescOnce sync.Once
file_transport_internet_dtls_config_proto_rawDescData = file_transport_internet_dtls_config_proto_rawDesc
)
func file_transport_internet_dtls_config_proto_rawDescGZIP() []byte {
file_transport_internet_dtls_config_proto_rawDescOnce.Do(func() {
file_transport_internet_dtls_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_dtls_config_proto_rawDescData)
})
return file_transport_internet_dtls_config_proto_rawDescData
}
var file_transport_internet_dtls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_transport_internet_dtls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_dtls_config_proto_goTypes = []any{
(DTLSMode)(0), // 0: v2ray.core.transport.internet.dtls.DTLSMode
(*Config)(nil), // 1: v2ray.core.transport.internet.dtls.Config
}
var file_transport_internet_dtls_config_proto_depIdxs = []int32{
0, // 0: v2ray.core.transport.internet.dtls.Config.mode:type_name -> v2ray.core.transport.internet.dtls.DTLSMode
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_dtls_config_proto_init() }
func file_transport_internet_dtls_config_proto_init() {
if File_transport_internet_dtls_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_dtls_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
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_dtls_config_proto_rawDesc,
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_dtls_config_proto_goTypes,
DependencyIndexes: file_transport_internet_dtls_config_proto_depIdxs,
EnumInfos: file_transport_internet_dtls_config_proto_enumTypes,
MessageInfos: file_transport_internet_dtls_config_proto_msgTypes,
}.Build()
File_transport_internet_dtls_config_proto = out.File
file_transport_internet_dtls_config_proto_rawDesc = nil
file_transport_internet_dtls_config_proto_goTypes = nil
file_transport_internet_dtls_config_proto_depIdxs = nil
}

View File

@ -0,0 +1,25 @@
syntax = "proto3";
package v2ray.core.transport.internet.dtls;
option csharp_namespace = "V2Ray.Core.Transport.Internet.Dtls";
option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/dtls";
option java_package = "com.v2ray.core.transport.internet.dtls";
option java_multiple_files = true;
import "common/protoext/extensions.proto";
enum DTLSMode {
INVALID = 0;
PSK = 1;
}
message Config {
option (v2ray.core.common.protoext.message_opt).type = "transport";
option (v2ray.core.common.protoext.message_opt).short_name = "dtls";
DTLSMode mode = 1;
bytes psk = 2;
uint32 mtu = 3;
uint32 replay_protection_window = 4;
}

View File

@ -0,0 +1,57 @@
package dtls
import (
"context"
"github.com/pion/dtls/v2"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/session"
"github.com/v2fly/v2ray-core/v5/transport/internet"
)
func dialDTLS(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
newError("dialing DTLS to ", dest).WriteToLog()
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
dialer := transportEnvironment.Dialer()
rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings)
if err != nil {
return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err)
}
config := &dtls.Config{}
config.MTU = int(transportConfiguration.Mtu)
config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow)
switch transportConfiguration.Mode {
case DTLSMode_PSK:
config.PSK = func(bytes []byte) ([]byte, error) {
return transportConfiguration.Psk, nil
}
config.PSKIdentityHint = []byte("")
config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256}
default:
return nil, newError("unknow dtls mode")
}
return dtls.Client(rawConn, config)
}
func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx))
conn, err := dialDTLS(ctx, dest, streamSettings)
if err != nil {
return nil, newError("failed to dial request to ", dest).Base(err)
}
return internet.Connection(conn), nil
}
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, dial))
}

View File

@ -0,0 +1,16 @@
package dtls
import (
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/transport/internet"
)
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
const protocolName = "dtls"
func init() {
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
return new(Config)
}))
}

View File

@ -0,0 +1,9 @@
package dtls
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,212 @@
package dtls
import (
"context"
"io"
gonet "net"
"sync"
"time"
"github.com/pion/dtls/v2"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/buf"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet"
"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
)
type Listener struct {
config *Config
sync.Mutex
addConn internet.ConnHandler
hub *udp.Hub
sessions map[ConnectionID]*dTLSConnWrapped
}
func (l *Listener) Close() error {
return l.hub.Close()
}
func (l *Listener) Addr() net.Addr {
return l.hub.Addr()
}
type ConnectionID struct {
Remote net.Address
Port net.Port
}
func newDTLSServerConn(src net.Destination, parent *Listener) *dTLSConn {
ctx := context.Background()
ctx, finish := context.WithCancel(ctx)
return &dTLSConn{
src: src,
parent: parent,
readChan: make(chan *buf.Buffer, 256),
ctx: ctx,
finish: finish,
}
}
type dTLSConnWrapped struct {
unencryptedConn *dTLSConn
dTLSConn *dtls.Conn
}
type dTLSConn struct {
src net.Destination
parent *Listener
readChan chan *buf.Buffer
ctx context.Context
finish func()
}
func (l *dTLSConn) Read(b []byte) (n int, err error) {
select {
case pack := <-l.readChan:
n := copy(b, pack.Bytes())
defer pack.Release()
if n < int(pack.Len()) {
return n, io.ErrShortBuffer
}
return n, nil
case <-l.ctx.Done():
return 0, l.ctx.Err()
}
}
func (l *dTLSConn) Write(b []byte) (n int, err error) {
return l.parent.hub.WriteTo(b, l.src)
}
func (l *dTLSConn) Close() error {
l.finish()
l.parent.Remove(l.src)
return nil
}
func (l *dTLSConn) LocalAddr() gonet.Addr {
return nil
}
func (l *dTLSConn) RemoteAddr() gonet.Addr {
return &net.UDPAddr{
IP: l.src.Address.IP(),
Port: int(l.src.Port.Value()),
}
}
func (l *dTLSConn) SetDeadline(t time.Time) error {
return nil
}
func (l *dTLSConn) SetReadDeadline(t time.Time) error {
return nil
}
func (l *dTLSConn) SetWriteDeadline(t time.Time) error {
return nil
}
func (l *dTLSConn) OnReceive(payload *buf.Buffer) {
select {
case l.readChan <- payload:
default:
}
}
func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) {
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
hub, err := udp.ListenUDP(ctx, address, port, streamSettings, udp.HubCapacity(1024))
if err != nil {
return nil, err
}
l := &Listener{
addConn: addConn,
config: transportConfiguration,
sessions: make(map[ConnectionID]*dTLSConnWrapped),
}
l.Lock()
l.hub = hub
l.Unlock()
newError("listening on ", address, ":", port).WriteToLog()
go l.handlePackets()
return l, err
}
func (l *Listener) handlePackets() {
receive := l.hub.Receive()
for payload := range receive {
l.OnReceive(payload.Payload, payload.Source)
}
}
func newDTLSConnWrapped(unencryptedConnection *dTLSConn, transportConfiguration *Config) (*dtls.Conn, error) {
config := &dtls.Config{}
config.MTU = int(transportConfiguration.Mtu)
config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow)
switch transportConfiguration.Mode {
case DTLSMode_PSK:
config.PSK = func(bytes []byte) ([]byte, error) {
return transportConfiguration.Psk, nil
}
config.PSKIdentityHint = []byte("")
config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256}
default:
newError("unknown dtls mode").WriteToLog()
}
dtlsConn, err := dtls.Server(unencryptedConnection, config)
if err != nil {
return nil, newError("unable to create dtls server conn").Base(err)
}
return dtlsConn, err
}
func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) {
id := ConnectionID{
Remote: src.Address,
Port: src.Port,
}
l.Lock()
defer l.Unlock()
conn, found := l.sessions[id]
if !found {
var err error
unEncryptedConn := newDTLSServerConn(src, l)
conn = &dTLSConnWrapped{unencryptedConn: unEncryptedConn}
l.sessions[id] = conn
go func() {
conn.dTLSConn, err = newDTLSConnWrapped(unEncryptedConn, l.config)
if err != nil {
newError("unable to accept new dtls connection").Base(err).WriteToLog()
return
}
l.addConn(internet.Connection(conn.dTLSConn))
}()
}
conn.unencryptedConn.OnReceive(payload)
}
func (l *Listener) Remove(src net.Destination) {
l.Lock()
defer l.Unlock()
id := ConnectionID{
Remote: src.Address,
Port: src.Port,
}
delete(l.sessions, id)
}
func ListenDTLS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
return NewListener(ctx, address, port, streamSettings, addConn)
}
func init() {
common.Must(internet.RegisterTransportListener(protocolName, ListenDTLS))
}

View File

@ -8,6 +8,8 @@ import (
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/buf"
"github.com/v2fly/v2ray-core/v5/common/dice"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet"
"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
@ -47,7 +49,10 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet
dest.Network = net.Network_UDP
newError("dialing mKCP to ", dest).WriteToLog()
rawConn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
dialer := transportEnvironment.Dialer()
rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings)
if err != nil {
return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err)
}

View File

@ -7,6 +7,11 @@ import (
"testing"
"time"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
"github.com/google/go-cmp/cmp"
"golang.org/x/sync/errgroup"
@ -18,7 +23,17 @@ import (
)
func TestDialAndListen(t *testing.T) {
listener, err := NewListener(context.Background(), net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{
ctx := context.Background()
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
proxyEnvironment := rootEnv.ProxyEnvironment("o")
transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp")
if err != nil {
t.Fatal(err)
}
ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment)
listener, err := NewListener(ctx, net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{
ProtocolName: "mkcp",
ProtocolSettings: &Config{},
}, func(conn internet.Connection) {
@ -45,7 +60,7 @@ func TestDialAndListen(t *testing.T) {
var errg errgroup.Group
for i := 0; i < 10; i++ {
errg.Go(func() error {
clientConn, err := DialKCP(context.Background(), net.UDPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
clientConn, err := DialKCP(ctx, net.UDPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
ProtocolName: "mkcp",
ProtocolSettings: &Config{},
})

View File

@ -0,0 +1,9 @@
package packetconn
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,342 @@
package packetconn
import (
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
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 ClientConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UnderlyingTransportSetting *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"`
UnderlyingTransportName string `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"`
MaxWriteDelay int32 `protobuf:"varint,3,opt,name=max_write_delay,json=maxWriteDelay,proto3" json:"max_write_delay,omitempty"`
MaxRequestSize int32 `protobuf:"varint,4,opt,name=max_request_size,json=maxRequestSize,proto3" json:"max_request_size,omitempty"`
PollingIntervalInitial int32 `protobuf:"varint,5,opt,name=polling_interval_initial,json=pollingIntervalInitial,proto3" json:"polling_interval_initial,omitempty"`
}
func (x *ClientConfig) Reset() {
*x = ClientConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ClientConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ClientConfig) ProtoMessage() {}
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_request_assembler_packetconn_packetConn_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 ClientConfig.ProtoReflect.Descriptor instead.
func (*ClientConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{0}
}
func (x *ClientConfig) GetUnderlyingTransportSetting() *anypb.Any {
if x != nil {
return x.UnderlyingTransportSetting
}
return nil
}
func (x *ClientConfig) GetUnderlyingTransportName() string {
if x != nil {
return x.UnderlyingTransportName
}
return ""
}
func (x *ClientConfig) GetMaxWriteDelay() int32 {
if x != nil {
return x.MaxWriteDelay
}
return 0
}
func (x *ClientConfig) GetMaxRequestSize() int32 {
if x != nil {
return x.MaxRequestSize
}
return 0
}
func (x *ClientConfig) GetPollingIntervalInitial() int32 {
if x != nil {
return x.PollingIntervalInitial
}
return 0
}
type ServerConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UnderlyingTransportSetting *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"`
UnderlyingTransportName string `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"`
MaxWriteSize int32 `protobuf:"varint,3,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"`
MaxWriteDurationMs int32 `protobuf:"varint,4,opt,name=max_write_duration_ms,json=maxWriteDurationMs,proto3" json:"max_write_duration_ms,omitempty"`
MaxSimultaneousWriteConnection int32 `protobuf:"varint,5,opt,name=max_simultaneous_write_connection,json=maxSimultaneousWriteConnection,proto3" json:"max_simultaneous_write_connection,omitempty"`
PacketWritingBuffer int32 `protobuf:"varint,6,opt,name=packet_writing_buffer,json=packetWritingBuffer,proto3" json:"packet_writing_buffer,omitempty"`
}
func (x *ServerConfig) Reset() {
*x = ServerConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerConfig) ProtoMessage() {}
func (x *ServerConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1]
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 ServerConfig.ProtoReflect.Descriptor instead.
func (*ServerConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{1}
}
func (x *ServerConfig) GetUnderlyingTransportSetting() *anypb.Any {
if x != nil {
return x.UnderlyingTransportSetting
}
return nil
}
func (x *ServerConfig) GetUnderlyingTransportName() string {
if x != nil {
return x.UnderlyingTransportName
}
return ""
}
func (x *ServerConfig) GetMaxWriteSize() int32 {
if x != nil {
return x.MaxWriteSize
}
return 0
}
func (x *ServerConfig) GetMaxWriteDurationMs() int32 {
if x != nil {
return x.MaxWriteDurationMs
}
return 0
}
func (x *ServerConfig) GetMaxSimultaneousWriteConnection() int32 {
if x != nil {
return x.MaxSimultaneousWriteConnection
}
return 0
}
func (x *ServerConfig) GetPacketWritingBuffer() int32 {
if x != nil {
return x.PacketWritingBuffer
}
return 0
}
var File_transport_internet_request_assembler_packetconn_packetConn_proto protoreflect.FileDescriptor
var file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc = []byte{
0x0a, 0x40, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x61, 0x73, 0x73,
0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e,
0x6e, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x3a, 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, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62,
0x6c, 0x65, 0x72, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 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, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe4, 0x02, 0x0a, 0x0c,
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x56, 0x0a, 0x1c,
0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x1a, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c,
0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x19, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69,
0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79,
0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65,
0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x57, 0x72,
0x69, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69,
0x7a, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x05,
0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74,
0x65, 0x72, 0x76, 0x61, 0x6c, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x3a, 0x34, 0x82, 0xb5,
0x18, 0x30, 0x0a, 0x22, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f,
0x6e, 0x6e, 0x22, 0xb0, 0x03, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x56, 0x0a, 0x1c, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e,
0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
0x1a, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x19, 0x75,
0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17,
0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x77,
0x72, 0x69, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0c, 0x6d, 0x61, 0x78, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x31, 0x0a,
0x15, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x6d, 0x61,
0x78, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73,
0x12, 0x49, 0x0a, 0x21, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x74, 0x61, 0x6e,
0x65, 0x6f, 0x75, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x6d, 0x61, 0x78,
0x53, 0x69, 0x6d, 0x75, 0x6c, 0x74, 0x61, 0x6e, 0x65, 0x6f, 0x75, 0x73, 0x57, 0x72, 0x69, 0x74,
0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x70,
0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x75,
0x66, 0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x70, 0x61, 0x63, 0x6b,
0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a,
0x34, 0x82, 0xb5, 0x18, 0x30, 0x0a, 0x22, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c,
0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x65,
0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x42, 0xcf, 0x01, 0x0a, 0x3e, 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, 0x72, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e, 0x70, 0x61,
0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x50, 0x01, 0x5a, 0x4e, 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, 0x72, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2f,
0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0xaa, 0x02, 0x3a, 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, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x63,
0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce sync.Once
file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData = file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc
)
func file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP() []byte {
file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce.Do(func() {
file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData)
})
return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData
}
var file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = []any{
(*ClientConfig)(nil), // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig
(*ServerConfig)(nil), // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig
(*anypb.Any)(nil), // 2: google.protobuf.Any
}
var file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = []int32{
2, // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig.underlying_transport_setting:type_name -> google.protobuf.Any
2, // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig.underlying_transport_setting:type_name -> google.protobuf.Any
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_transport_internet_request_assembler_packetconn_packetConn_proto_init() }
func file_transport_internet_request_assembler_packetconn_packetConn_proto_init() {
if File_transport_internet_request_assembler_packetconn_packetConn_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*ClientConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*ServerConfig); 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_request_assembler_packetconn_packetConn_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes,
DependencyIndexes: file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs,
MessageInfos: file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes,
}.Build()
File_transport_internet_request_assembler_packetconn_packetConn_proto = out.File
file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc = nil
file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = nil
file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = nil
}

View File

@ -0,0 +1,35 @@
syntax = "proto3";
package v2ray.core.transport.internet.request.assembler.packetconn;
option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Assembler.Packetconn";
option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn";
option java_package = "com.v2ray.core.transport.internet.request.assembler.packetconn";
option java_multiple_files = true;
import "common/protoext/extensions.proto";
import "google/protobuf/any.proto";
message ClientConfig {
option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.client";
option (v2ray.core.common.protoext.message_opt).short_name = "packetconn";
google.protobuf.Any underlying_transport_setting = 1;
string underlying_transport_name = 2;
int32 max_write_delay = 3;
int32 max_request_size = 4;
int32 polling_interval_initial = 5;
}
message ServerConfig {
option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.server";
option (v2ray.core.common.protoext.message_opt).short_name = "packetconn";
google.protobuf.Any underlying_transport_setting = 1;
string underlying_transport_name = 2;
int32 max_write_size = 3;
int32 max_write_duration_ms = 4;
int32 max_simultaneous_write_connection = 5;
int32 packet_writing_buffer = 6;
}

View File

@ -0,0 +1,48 @@
package packetconn
import (
"encoding/binary"
"io"
)
func NewPacketBundle() PacketBundle {
return &packetBundle{}
}
type packetBundle struct{}
func (p *packetBundle) Overhead() int {
return 2
}
func (p *packetBundle) WriteToBundle(b []byte, writer io.Writer) (err error) {
err = binary.Write(writer, binary.BigEndian, uint16(len(b)))
if err != nil {
return
}
_, err = writer.Write(b)
return
}
func (p *packetBundle) ReadFromBundle(writer io.Reader) (b []byte, err error) {
var length uint16
err = binary.Read(writer, binary.BigEndian, &length)
if err != nil {
return
}
b = make([]byte, length)
n, err := io.ReadFull(writer, b)
if err != nil {
return
}
if n != int(length) {
return nil, io.ErrUnexpectedEOF
}
return
}
type PacketBundle interface {
Overhead() int
WriteToBundle(b []byte, writer io.Writer) (err error)
ReadFromBundle(writer io.Reader) (b []byte, err error)
}

View File

@ -0,0 +1,3 @@
package packetconn
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen

View File

@ -0,0 +1,429 @@
package packetconn
import (
"bytes"
"context"
"crypto/rand"
"io"
"time"
"github.com/golang-collections/go-datastructures/queue"
"github.com/v2fly/v2ray-core/v5/transport/internet/request"
)
func newRequestToPacketConnClient(ctx context.Context, config *ClientConfig) (*requestToPacketConnClient, error) { //nolint: unparam
return &requestToPacketConnClient{ctx: ctx, config: config}, nil
}
type requestToPacketConnClient struct {
assembly request.TransportClientAssembly
ctx context.Context
config *ClientConfig
}
func (r *requestToPacketConnClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) {
r.assembly = assembly
}
func (r *requestToPacketConnClient) Dial() (io.ReadWriteCloser, error) {
sessionID := make([]byte, 16)
_, err := rand.Read(sessionID)
if err != nil {
return nil, err
}
ctxWithCancel, cancel := context.WithCancel(r.ctx)
clientSess := &requestToPacketConnClientSession{
sessionID: sessionID,
currentPollingInterval: int(r.config.PollingIntervalInitial),
maxRequestSize: int(r.config.MaxRequestSize),
maxWriteDelay: int(r.config.MaxWriteDelay),
assembly: r.assembly,
writerChan: make(chan []byte, 256),
readerChan: make(chan []byte, 256),
ctx: ctxWithCancel,
finish: cancel,
}
go clientSess.keepRunning()
return clientSess, nil
}
type requestToPacketConnClientSession struct {
sessionID []byte
currentPollingInterval int
maxRequestSize int
maxWriteDelay int
assembly request.TransportClientAssembly
writerChan chan []byte
readerChan chan []byte
ctx context.Context
finish func()
nextWrite []byte
}
func (r *requestToPacketConnClientSession) keepRunning() {
for r.ctx.Err() == nil {
r.runOnce()
}
}
func (r *requestToPacketConnClientSession) runOnce() {
requestBody := bytes.NewBuffer(nil)
waitTimer := time.NewTimer(time.Duration(r.currentPollingInterval) * time.Millisecond)
var seenPacket bool
packetBundler := NewPacketBundle()
copyFromChan:
for {
select {
case <-r.ctx.Done():
return
case <-waitTimer.C:
break copyFromChan
case packet := <-r.writerChan:
if !seenPacket {
seenPacket = true
waitTimer.Stop()
waitTimer.Reset(time.Duration(r.maxWriteDelay) * time.Millisecond)
}
sizeOffset := packetBundler.Overhead() + len(packet)
if requestBody.Len()+sizeOffset > r.maxRequestSize {
r.nextWrite = packet
break copyFromChan
}
err := packetBundler.WriteToBundle(packet, requestBody)
if err != nil {
newError("failed to write to bundle").Base(err).WriteToLog()
}
}
}
waitTimer.Stop()
go func() {
reader, writer := io.Pipe()
streamingRespOpt := &pipedStreamingRespOption{writer}
go func() {
for {
if packet, err := packetBundler.ReadFromBundle(reader); err == nil {
r.readerChan <- packet
} else {
return
}
}
}()
resp, err := r.assembly.Tripper().RoundTrip(r.ctx, request.Request{Data: requestBody.Bytes(), ConnectionTag: r.sessionID},
streamingRespOpt)
if err != nil {
newError("failed to roundtrip").Base(err).WriteToLog()
if r.ctx.Err() != nil {
return
}
}
if resp.Data != nil && len(resp.Data) != 0 {
respReader := bytes.NewReader(resp.Data)
for respReader.Len() != 0 {
packet, err := packetBundler.ReadFromBundle(respReader)
if err != nil {
newError("failed to read from bundle").Base(err).WriteToLog()
if r.ctx.Err() != nil {
return
}
}
r.readerChan <- packet
}
}
}()
}
type pipedStreamingRespOption struct {
writer *io.PipeWriter
}
func (p *pipedStreamingRespOption) RoundTripperOption() {
}
func (p *pipedStreamingRespOption) GetResponseWriter() io.Writer {
return p.writer
}
func (r *requestToPacketConnClientSession) Write(p []byte) (n int, err error) {
buf := make([]byte, len(p))
copy(buf, p)
select {
case <-r.ctx.Done():
return 0, r.ctx.Err()
case r.writerChan <- buf:
return len(p), nil
}
}
func (r *requestToPacketConnClientSession) Read(p []byte) (n int, err error) {
select {
case <-r.ctx.Done():
return 0, r.ctx.Err()
case buf := <-r.readerChan:
copy(p, buf)
return len(buf), nil
}
}
func (r *requestToPacketConnClientSession) Close() error {
r.finish()
return nil
}
func newRequestToPacketConnServer(ctx context.Context, config *ServerConfig) *requestToPacketConnServer {
return &requestToPacketConnServer{
sessionMap: make(map[string]*requestToPacketConnServerSession),
ctx: ctx,
config: config,
}
}
type requestToPacketConnServer struct {
packetSessionReceiver request.SessionReceiver
sessionMap map[string]*requestToPacketConnServerSession
ctx context.Context
config *ServerConfig
}
func (r *requestToPacketConnServer) onSessionReceiverReady(sessrecv request.SessionReceiver) {
r.packetSessionReceiver = sessrecv
}
func (r *requestToPacketConnServer) OnRoundTrip(ctx context.Context, req request.Request,
opts ...request.RoundTripperOption,
) (resp request.Response, err error) {
SessionID := req.ConnectionTag
if SessionID == nil {
return request.Response{}, newError("nil session id")
}
sessionID := string(SessionID)
session, found := r.sessionMap[sessionID]
if !found {
ctxWithFinish, finish := context.WithCancel(ctx)
session = &requestToPacketConnServerSession{
SessionID: SessionID,
writingConnectionQueue: queue.New(64),
writerChan: make(chan []byte, int(r.config.PacketWritingBuffer)),
readerChan: make(chan []byte, 256),
ctx: ctxWithFinish,
finish: finish,
server: r,
maxWriteSize: int(r.config.MaxWriteSize),
maxWriteDuration: int(r.config.MaxWriteDurationMs),
maxSimultaneousWriteConnection: int(r.config.MaxSimultaneousWriteConnection),
}
r.sessionMap[sessionID] = session
err = r.packetSessionReceiver.OnNewSession(ctx, session)
}
if err != nil {
return request.Response{}, err
}
return session.OnRoundTrip(ctx, req, opts...)
}
func (r *requestToPacketConnServer) removeSessionID(sessionID []byte) {
delete(r.sessionMap, string(sessionID))
}
type requestToPacketConnServerSession struct {
SessionID []byte
writingConnectionQueue *queue.Queue
writerChan chan []byte
readerChan chan []byte
ctx context.Context
finish func()
server *requestToPacketConnServer
maxWriteSize int
maxWriteDuration int
maxSimultaneousWriteConnection int
}
func (r *requestToPacketConnServerSession) Read(p []byte) (n int, err error) {
select {
case <-r.ctx.Done():
return 0, r.ctx.Err()
case buf := <-r.readerChan:
copy(p, buf)
return len(buf), nil
}
}
var debugStats struct {
packetWritten int
packetDropped int
}
/*
var _ = func() bool {
go func() {
for {
time.Sleep(time.Second)
newError("packet written: ", debugStats.packetWritten, " packet dropped: ", debugStats.packetDropped).WriteToLog()
}
}()
return true
}()*/
func (r *requestToPacketConnServerSession) Write(p []byte) (n int, err error) {
buf := make([]byte, len(p))
copy(buf, p)
select {
case <-r.ctx.Done():
return 0, r.ctx.Err()
case r.writerChan <- buf:
debugStats.packetWritten++
return len(p), nil
default: // This write will be called from global listener's routine, it must not block
debugStats.packetDropped++
return len(p), nil
}
}
func (r *requestToPacketConnServerSession) Close() error {
r.server.removeSessionID(r.SessionID)
r.finish()
return nil
}
type writingConnection struct {
focus func()
finish func()
finishCtx context.Context
}
func (r *requestToPacketConnServerSession) OnRoundTrip(ctx context.Context, req request.Request,
opts ...request.RoundTripperOption,
) (resp request.Response, err error) {
// TODO: fix connection graceful close
var streamingRespWriter io.Writer
var streamingRespWriterFlusher request.OptionSupportsStreamingResponseExtensionFlusher
for _, opt := range opts {
if streamingRespOpt, ok := opt.(request.OptionSupportsStreamingResponse); ok {
streamingRespWriter = streamingRespOpt.GetResponseWriter()
if streamingRespWriterFlusherOpt, ok := opt.(request.OptionSupportsStreamingResponseExtensionFlusher); ok {
streamingRespWriterFlusher = streamingRespWriterFlusherOpt
}
}
}
packetBundler := NewPacketBundle()
reqReader := bytes.NewReader(req.Data)
for reqReader.Len() != 0 {
packet, err := packetBundler.ReadFromBundle(reqReader)
if err != nil {
err = newError("failed to read from bundle").Base(err)
return request.Response{}, err
}
r.readerChan <- packet
}
onFocusCtx, focus := context.WithCancel(ctx)
onFinishCtx, finish := context.WithCancel(ctx)
r.writingConnectionQueue.Put(&writingConnection{
focus: focus,
finish: finish,
finishCtx: onFinishCtx,
})
amountToEnd := r.writingConnectionQueue.Len() - int64(r.maxSimultaneousWriteConnection)
for amountToEnd > 0 {
{
_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
i.(*writingConnection).finish()
amountToEnd--
return amountToEnd > 0
})
}
}
{
_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
i.(*writingConnection).focus()
return false
})
}
bufferedRespWriter := bytes.NewBuffer(nil)
finishWrite := func() {
resp.Data = bufferedRespWriter.Bytes()
{
_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
i.(*writingConnection).focus()
if i.(*writingConnection).finishCtx.Err() != nil { //nolint: gosimple
return true
}
return false
})
}
}
progressiveSend := streamingRespWriter != nil
var respWriter io.Writer
if progressiveSend {
respWriter = streamingRespWriter
} else {
respWriter = bufferedRespWriter
}
var bytesSent int
onReceivePacket := func(packet []byte) bool {
bytesSent += len(packet) + packetBundler.Overhead()
err := packetBundler.WriteToBundle(packet, respWriter)
if err != nil {
newError("failed to write to bundle").Base(err).WriteToLog()
}
if streamingRespWriterFlusher != nil {
streamingRespWriterFlusher.Flush()
}
if bytesSent >= r.maxWriteSize {
return false
}
return true
}
finishWriteTimer := time.NewTimer(time.Millisecond * time.Duration(r.maxWriteDuration))
if !progressiveSend {
select {
case <-onFocusCtx.Done():
case <-onFinishCtx.Done():
finishWrite()
return resp, nil
}
} else {
select {
case <-onFinishCtx.Done():
finishWrite()
return resp, nil
default:
}
}
firstRead := true
for {
select {
case <-onFinishCtx.Done():
finishWrite()
finishWriteTimer.Stop()
return resp, nil
case packet := <-r.writerChan:
keepSending := onReceivePacket(packet)
if firstRead {
firstRead = false
}
if !keepSending {
finishWrite()
finishWriteTimer.Stop()
return resp, nil
}
case <-finishWriteTimer.C:
finishWrite()
return resp, nil
}
}
}

View File

@ -0,0 +1,78 @@
package packetconn
import (
"golang.org/x/net/context"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/serial"
"github.com/v2fly/v2ray-core/v5/transport/internet"
)
type wrappedTransportEnvironment struct {
environment.TransportEnvironment
client *requestToPacketConnClient
server *requestToPacketConnServer
}
func (w *wrappedTransportEnvironment) Listen(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.Listener, error) {
return nil, newError("not implemented")
}
func (w *wrappedTransportEnvironment) ListenPacket(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.PacketConn, error) {
packetConn := newWrappedPacketConn(ctx)
w.server.onSessionReceiverReady(packetConn)
return packetConn, nil
}
func (w *wrappedTransportEnvironment) Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *internet.SocketConfig) (net.Conn, error) {
session, err := w.client.Dial()
if err != nil {
return nil, err
}
return newWrappedConn(session), nil
}
func (w *wrappedTransportEnvironment) Dialer() internet.SystemDialer {
return w
}
func (w *wrappedTransportEnvironment) Listener() internet.SystemListener {
return w
}
func newUDPAssemblerServerFromConfig(ctx context.Context, config *ServerConfig) (*udpAssemblerServer, error) {
instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting)
if err != nil {
return nil, newError("failed to get instance of underlying transport").Base(err).AtError()
}
memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance}
return newUDPAssemblerServer(ctx, config, memcfg), nil
}
func newUDPAssemblerClientFromConfig(ctx context.Context, config *ClientConfig) (*udpAssemblerClient, error) {
instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting)
if err != nil {
return nil, newError("failed to get instance of underlying transport").Base(err).AtError()
}
memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance}
return newUDPAssemblerClient(ctx, config, memcfg), nil
}
func init() {
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
serverConfig, ok := config.(*ServerConfig)
if !ok {
return nil, newError("not a ServerConfig")
}
return newUDPAssemblerServerFromConfig(ctx, serverConfig)
}))
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
clientConfig, ok := config.(*ClientConfig)
if !ok {
return nil, newError("not a ClientConfig")
}
return newUDPAssemblerClientFromConfig(ctx, clientConfig)
}))
}

View File

@ -0,0 +1,81 @@
package packetconn
import (
"io"
gonet "net"
"sync"
"time"
"golang.org/x/net/context"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet"
"github.com/v2fly/v2ray-core/v5/transport/internet/request"
)
type udpAssemblerClient struct {
ctx context.Context
streamSettings *internet.MemoryStreamConfig
assembly request.TransportClientAssembly
req2connc *requestToPacketConnClient
}
func (u *udpAssemblerClient) NewSession(ctx context.Context, opts ...request.SessionOption) (request.Session, error) {
return u.dial(net.Destination{})
}
func (u *udpAssemblerClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) {
u.assembly = assembly
u.req2connc.OnTransportClientAssemblyReady(assembly)
}
func newWrappedConn(in io.ReadWriteCloser) net.Conn {
return wrappedConn{in}
}
type wrappedConn struct {
io.ReadWriteCloser
}
func (w wrappedConn) LocalAddr() gonet.Addr {
return nil
}
func (w wrappedConn) RemoteAddr() gonet.Addr {
return nil
}
func (w wrappedConn) SetDeadline(t time.Time) error {
return nil
}
func (w wrappedConn) SetReadDeadline(t time.Time) error {
return nil
}
func (w wrappedConn) SetWriteDeadline(t time.Time) error {
return nil
}
func newWrappedPacketConn(ctx context.Context) *wrappedPacketConn {
ctxWithCancel, cancel := context.WithCancel(ctx)
return &wrappedPacketConn{
conn: make(map[string]*serverSession),
readChan: make(chan packet, 16), ctx: ctxWithCancel, finish: cancel, connLock: &sync.Mutex{},
}
}
func newUDPAssemblerClient(ctx context.Context, config *ClientConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerClient {
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment}
transportEnvironmentWrapped.client, _ = newRequestToPacketConnClient(ctx, config)
wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped)
return &udpAssemblerClient{ctx: wrappedContext, streamSettings: streamSettings, req2connc: transportEnvironmentWrapped.client}
}
func (u *udpAssemblerClient) dial(dest net.Destination) (internet.Connection, error) {
_ = dest
return internet.Dial(u.ctx, net.TCPDestination(net.LocalHostIP, 0), u.streamSettings)
}

View File

@ -0,0 +1,166 @@
package packetconn
import (
"crypto/rand"
"io"
"net"
"sync"
"time"
"golang.org/x/net/context"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
net2 "github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/transport/internet"
"github.com/v2fly/v2ray-core/v5/transport/internet/request"
)
type packet struct {
addr string
data []byte
}
type wrappedPacketConn struct {
connLock *sync.Mutex
conn map[string]*serverSession
readChan chan packet
ctx context.Context
finish func()
}
func (w *wrappedPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
select {
case pack := <-w.readChan:
n := copy(p, pack.data)
if n < len(pack.data) {
return n, nil, io.ErrShortBuffer
}
return n, &net.UDPAddr{IP: net2.IP(pack.addr)}, nil
case <-w.ctx.Done():
return 0, nil, w.ctx.Err()
}
}
func (w *wrappedPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
w.connLock.Lock()
conn := w.conn[string(addr.(*net.UDPAddr).IP)]
w.connLock.Unlock()
return conn.Write(p)
}
func (w *wrappedPacketConn) Close() error {
w.finish()
return nil
}
func (w *wrappedPacketConn) LocalAddr() net.Addr {
return nil
}
func (w *wrappedPacketConn) SetDeadline(t time.Time) error {
return nil
}
func (w *wrappedPacketConn) SetReadDeadline(t time.Time) error {
return nil
}
func (w *wrappedPacketConn) SetWriteDeadline(t time.Time) error {
return nil
}
func (w wrappedPacketConn) OnNewSession(ctx context.Context, sess request.Session, opts ...request.SessionOption) error {
imaginaryAddr := net2.UDPAddr{
IP: net2.AnyIPv6.IP(),
Port: 0,
}
rand.Read([]byte(imaginaryAddr.IP))
session := newServerSession(ctx, sess, string(imaginaryAddr.IP), &w)
w.connLock.Lock()
w.conn[string(imaginaryAddr.IP)] = session
w.connLock.Unlock()
session.start()
return nil
}
func newServerSession(ctx context.Context, sess request.Session, name string, listener *wrappedPacketConn) *serverSession {
_ = ctx
return &serverSession{session: sess, name: name, listener: listener}
}
type serverSession struct {
name string
session request.Session
listener *wrappedPacketConn
}
func (s *serverSession) start() {
go func() {
for {
select {
case <-s.listener.ctx.Done():
return
default:
buf := make([]byte, 2000)
n, err := s.session.Read(buf)
if err != nil || n > 2000 {
return
}
s.listener.readChan <- packet{s.name, buf[:n]}
}
}
}()
}
func (s *serverSession) Write(p []byte) (int, error) {
return s.session.Write(p)
}
type udpAssemblerServer struct {
ctx context.Context
streamSettings *internet.MemoryStreamConfig
assembly request.TransportServerAssembly
req2packs *requestToPacketConnServer
listener internet.Listener
}
func (u *udpAssemblerServer) Start() error {
listener, err := u.listen(net2.LocalHostIP, 0)
if err != nil {
return newError("failed to listen").Base(err).AtError()
}
u.listener = listener
return nil
}
func (u *udpAssemblerServer) Close() error {
return u.listener.Close()
}
func (u *udpAssemblerServer) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
return u.req2packs.OnRoundTrip(ctx, req, opts...)
}
func (u *udpAssemblerServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) {
u.assembly = assembly
}
func newUDPAssemblerServer(ctx context.Context, config *ServerConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerServer {
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment}
transportEnvironmentWrapped.server = newRequestToPacketConnServer(ctx, config)
wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped)
return &udpAssemblerServer{ctx: wrappedContext, streamSettings: streamSettings, req2packs: transportEnvironmentWrapped.server}
}
func (u *udpAssemblerServer) listen(address net2.Address, port net2.Port) (internet.Listener, error) {
return internet.ListenTCP(u.ctx, address, port, u.streamSettings, func(connection internet.Connection) {
err := u.assembly.SessionReceiver().OnNewSession(u.ctx, connection)
if err != nil {
newError("failed to handle new session").Base(err).WriteToLog()
}
})
}

View File

@ -36,6 +36,11 @@ func (s server) Close() error {
if err := s.tripper.Close(); err != nil {
return newError("failed to close tripper").Base(err)
}
if runnableAssembler, ok := s.assembler.(common.Runnable); ok {
if err := runnableAssembler.Close(); err != nil {
return newError("failed to close assembler").Base(err)
}
}
return nil
}
@ -127,6 +132,12 @@ func listenRequest(ctx context.Context, address net.Address, port net.Port, stre
return nil, newError("failed to start tripper").Base(err)
}
if runnableAssembler, ok := serverAssembly.assembler.(common.Runnable); ok {
if err := runnableAssembler.Start(); err != nil {
return nil, newError("failed to start assembler").Base(err)
}
}
return serverAssembly, nil
}

View File

@ -2,6 +2,7 @@ package request
import (
"context"
"io"
"github.com/v2fly/v2ray-core/v5/common"
)
@ -36,3 +37,12 @@ type Request struct {
type Response struct {
Data []byte
}
type OptionSupportsStreamingResponse interface {
RoundTripperOption
GetResponseWriter() io.Writer
}
type OptionSupportsStreamingResponseExtensionFlusher interface {
Flush()
}

View File

@ -20,7 +20,9 @@ type ClientConfig struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Http *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"`
Http *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"`
AllowHttp bool `protobuf:"varint,2,opt,name=allow_http,json=allowHttp,proto3" json:"allow_http,omitempty"`
H2PoolSize int32 `protobuf:"varint,3,opt,name=h2_pool_size,json=h2PoolSize,proto3" json:"h2_pool_size,omitempty"`
}
func (x *ClientConfig) Reset() {
@ -62,6 +64,20 @@ func (x *ClientConfig) GetHttp() *HTTPConfig {
return nil
}
func (x *ClientConfig) GetAllowHttp() bool {
if x != nil {
return x.AllowHttp
}
return false
}
func (x *ClientConfig) GetH2PoolSize() int32 {
if x != nil {
return x.H2PoolSize
}
return 0
}
type ServerConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -184,48 +200,52 @@ var file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc = [
0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65,
0x72, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 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, 0x22, 0x9e, 0x01, 0x0a, 0x0c, 0x43,
0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdf, 0x01, 0x0a, 0x0c, 0x43,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x04, 0x68,
0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 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, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x12, 0x06, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x0c,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x04,
0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, 0x32, 0x72,
0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f,
0x68, 0x74, 0x74, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x48, 0x74, 0x74, 0x70, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x32, 0x5f, 0x70, 0x6f, 0x6f, 0x6c,
0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x32, 0x50,
0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x3a, 0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x63, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x12, 0x06, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x22, 0xd5, 0x01, 0x0a,
0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a,
0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 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, 0x72, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72,
0x2e, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x5f, 0x64,
0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6e, 0x6f, 0x44, 0x65, 0x63,
0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x3a,
0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72,
0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x06, 0x68, 0x74,
0x74, 0x70, 0x72, 0x74, 0x22, 0x3e, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65,
0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72,
0x65, 0x66, 0x69, 0x78, 0x42, 0xcc, 0x01, 0x0a, 0x3d, 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, 0x72, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e,
0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x5f, 0x64, 0x65,
0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6e, 0x6f, 0x44, 0x65, 0x63, 0x6f,
0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x3a, 0x33,
0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69,
0x70, 0x70, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x06, 0x68, 0x74, 0x74,
0x70, 0x72, 0x74, 0x22, 0x3e, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65, 0x66,
0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65,
0x66, 0x69, 0x78, 0x42, 0xcc, 0x01, 0x0a, 0x3d, 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, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x4d, 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, 0x72, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2f,
0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0xaa, 0x02, 0x39, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52,
0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x74, 0x70,
0x72, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x4d, 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, 0x72, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72,
0x2f, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0xaa, 0x02, 0x39, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
0x52, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x74,
0x70, 0x72, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -13,6 +13,8 @@ message ClientConfig {
option (v2ray.core.common.protoext.message_opt).short_name = "httprt";
HTTPConfig http = 1;
bool allow_http = 2;
int32 h2_pool_size = 3;
}
message ServerConfig {

View File

@ -40,31 +40,55 @@ func (h *httpTripperClient) OnTransportClientAssemblyReady(assembly request.Tran
}
func (h *httpTripperClient) RoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
var streamingWriter io.Writer
for _, v := range opts {
if streamingResp, ok := v.(request.OptionSupportsStreamingResponse); ok {
streamingWriter = streamingResp.GetResponseWriter()
}
}
if h.httpRTT == nil {
h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripper(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) {
var backDrop http.RoundTripper = unimplementedBackDrop{}
if h.config.AllowHttp {
backDrop = &http.Transport{
DialContext: func(_ context.Context, network, addr string) (gonet.Conn, error) {
return h.assembly.AutoImplDialer().Dial(ctx)
},
DialTLSContext: func(_ context.Context, network, addr string) (gonet.Conn, error) {
return nil, newError("unexpected dial of TLS")
},
}
}
h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) {
return h.assembly.AutoImplDialer().Dial(ctx)
}, unimplementedBackDrop{})
}, backDrop, int(h.config.H2PoolSize))
}
connectionTagStr := base64.RawURLEncoding.EncodeToString(req.ConnectionTag)
httpRequest, err := http.NewRequest("POST", h.config.Http.UrlPrefix+h.config.Http.Path, bytes.NewReader(req.Data))
if err != nil {
return
return resp, err
}
httpRequest.Header.Set("X-Session-ID", connectionTagStr)
httpResp, err := h.httpRTT.RoundTrip(httpRequest)
if err != nil {
return
return resp, err
}
defer httpResp.Body.Close()
result, err := io.ReadAll(httpResp.Body)
if err != nil {
return
if streamingWriter == nil {
result, err := io.ReadAll(httpResp.Body)
if err != nil {
return request.Response{}, err
}
return request.Response{Data: result}, err
}
return request.Response{Data: result}, err
_, err = io.Copy(streamingWriter, httpResp.Body)
if err != nil {
return request.Response{}, newError("unable to copy response").Base(err)
}
return request.Response{}, nil
}
func newHTTPRoundTripperServer(ctx context.Context, config *ServerConfig) request.RoundTripperServer {
@ -87,6 +111,25 @@ func (h *httpTripperServer) ServeHTTP(writer http.ResponseWriter, r *http.Reques
h.onRequest(writer, r)
}
type httpRespStreamWriting struct {
resp http.ResponseWriter
used bool
}
func (h *httpRespStreamWriting) RoundTripperOption() {
}
func (h *httpRespStreamWriting) GetResponseWriter() io.Writer {
h.used = true
return h.resp
}
func (h *httpRespStreamWriting) Flush() {
if f, ok := h.resp.(http.Flusher); ok {
f.Flush()
}
}
func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Request) {
tail := req.Header.Get("X-Session-ID")
data := []byte(tail)
@ -103,10 +146,16 @@ func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Reques
if err != nil {
newError("unable to read body").Base(err).AtInfo().WriteToLog()
}
recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data})
streamingRespOption := &httpRespStreamWriting{resp: resp}
recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data},
streamingRespOption)
if err != nil {
newError("unable to process roundtrip").Base(err).AtInfo().WriteToLog()
}
if streamingRespOption.used {
return
}
_, err = io.Copy(resp, bytes.NewReader(recvResp.Data))
if err != nil {
newError("unable to send response").Base(err).AtInfo().WriteToLog()

View File

@ -5,6 +5,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"os"
"strings"
"sync"
"time"
@ -194,6 +195,16 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
return nil
}
type alwaysFlushWriter struct {
file *os.File
}
func (a *alwaysFlushWriter) Write(p []byte) (n int, err error) {
n, err = a.file.Write(p)
a.file.Sync()
return n, err
}
// GetTLSConfig converts this Config into tls.Config.
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
root, err := c.getCertPool()

View File

@ -17,10 +17,17 @@ import (
type DialerFunc func(ctx context.Context, addr string) (net.Conn, error)
// NewALPNAwareHTTPRoundTripper creates an instance of RoundTripper that dial to remote HTTPS endpoint with
// an alternative version of TLS implementation.
func NewALPNAwareHTTPRoundTripper(ctx context.Context, dialer DialerFunc,
backdropTransport http.RoundTripper,
) http.RoundTripper {
return NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, dialer, backdropTransport, 1)
}
// NewALPNAwareHTTPRoundTripperWithH2Pool creates an instance of RoundTripper that dial to remote HTTPS endpoint with
// an alternative version of TLS implementation.
func NewALPNAwareHTTPRoundTripperWithH2Pool(ctx context.Context, dialer DialerFunc,
backdropTransport http.RoundTripper,
h2PoolSize int,
) http.RoundTripper {
rtImpl := &alpnAwareHTTPRoundTripperImpl{
connectWithH1: map[string]bool{},
@ -46,6 +53,8 @@ type alpnAwareHTTPRoundTripperImpl struct {
ctx context.Context
dialer DialerFunc
h2PoolSize int
}
type pendingConnKey struct {
@ -175,10 +184,20 @@ func (r *alpnAwareHTTPRoundTripperImpl) dialTLS(ctx context.Context, addr string
}
func (r *alpnAwareHTTPRoundTripperImpl) init() {
r.httpsH2Transport = &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
},
if r.h2PoolSize >= 2 {
r.httpsH2Transport = newH2TransportPool(int64(r.h2PoolSize), func() *http2.Transport {
return &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
},
}
})
} else {
r.httpsH2Transport = &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
},
}
}
r.httpsH1Transport = &http.Transport{
DialTLSContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
@ -220,3 +239,29 @@ func (c *unclaimedConnection) tick() {
c.Conn = nil
}
}
type h2TransportFactory func() *http2.Transport
func newH2TransportPool(size int64, h2factory h2TransportFactory) *h2TransportPool {
return &h2TransportPool{
pool: make([]*http2.Transport, size),
size: size,
h2factory: h2factory,
}
}
type h2TransportPool struct {
pool []*http2.Transport
h2factory h2TransportFactory
usageCount int64
size int64
}
func (h *h2TransportPool) RoundTrip(request *http.Request) (*http.Response, error) {
currentSlot := h.usageCount % h.size
h.usageCount++
if h.pool[currentSlot] == nil {
h.pool[currentSlot] = h.h2factory()
}
return h.pool[currentSlot].RoundTrip(request)
}

View File

@ -3,6 +3,9 @@ package udp
import (
"context"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
"github.com/v2fly/v2ray-core/v5/common/buf"
"github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/protocol/udp"
@ -25,6 +28,7 @@ func HubReceiveOriginalDestination(r bool) HubOption {
type Hub struct {
conn *net.UDPConn
connPacket net.PacketConn
cache chan *udp.Packet
capacity int
recvOrigDest bool
@ -46,8 +50,9 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
if sockopt != nil && sockopt.ReceiveOriginalDestAddress {
hub.recvOrigDest = true
}
udpConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
listener := transportEnvironment.Listener()
udpConn, err := listener.ListenPacket(ctx, &net.UDPAddr{
IP: address.IP(),
Port: int(port),
}, sockopt)
@ -55,7 +60,12 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
return nil, err
}
newError("listening UDP on ", address, ":", port).WriteToLog()
hub.conn = udpConn.(*net.UDPConn)
if udpConnDirect, ok := udpConn.(*net.UDPConn); ok {
hub.conn = udpConnDirect
} else {
hub.connPacket = udpConn
}
hub.cache = make(chan *udp.Packet, hub.capacity)
go hub.start()
@ -64,11 +74,21 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
// Close implements net.Listener.
func (h *Hub) Close() error {
if h.connPacket != nil {
h.connPacket.Close()
return nil
}
h.conn.Close()
return nil
}
func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) {
if h.connPacket != nil {
return h.connPacket.WriteTo(payload, &net.UDPAddr{
IP: dest.Address.IP(),
Port: int(dest.Port),
})
}
return h.conn.WriteToUDP(payload, &net.UDPAddr{
IP: dest.Address.IP(),
Port: int(dest.Port),
@ -83,47 +103,76 @@ func (h *Hub) start() {
for {
buffer := buf.New()
var noob int
var addr *net.UDPAddr
rawBytes := buffer.Extend(buf.Size)
n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes)
if err != nil {
newError("failed to read UDP msg").Base(err).WriteToLog()
buffer.Release()
break
}
buffer.Resize(0, int32(n))
if buffer.IsEmpty() {
buffer.Release()
continue
}
payload := &udp.Packet{
Payload: buffer,
Source: net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)),
}
if h.recvOrigDest && noob > 0 {
payload.Target = RetrieveOriginalDest(oobBytes[:noob])
if payload.Target.IsValid() {
newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog()
} else {
newError("failed to read UDP original destination").WriteToLog()
if h.conn != nil {
var noob int
var addr *net.UDPAddr
rawBytes := buffer.Extend(buf.Size)
n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes)
if err != nil {
newError("failed to read UDP msg").Base(err).WriteToLog()
buffer.Release()
break
}
}
buffer.Resize(0, int32(n))
select {
case c <- payload:
default:
buffer.Release()
payload.Payload = nil
if buffer.IsEmpty() {
buffer.Release()
continue
}
payload := &udp.Packet{
Payload: buffer,
Source: net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)),
}
if h.recvOrigDest && noob > 0 {
payload.Target = RetrieveOriginalDest(oobBytes[:noob])
if payload.Target.IsValid() {
newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog()
} else {
newError("failed to read UDP original destination").WriteToLog()
}
}
select {
case c <- payload:
default:
buffer.Release()
payload.Payload = nil
}
} else {
rawBytes := buffer.Extend(buf.Size)
n, addr, err := h.connPacket.ReadFrom(rawBytes)
if err != nil {
newError("failed to read UDP msg").Base(err).WriteToLog()
buffer.Release()
break
}
buffer.Resize(0, int32(n))
if buffer.IsEmpty() {
buffer.Release()
continue
}
payload := &udp.Packet{
Payload: buffer,
Source: net.DestinationFromAddr(addr),
}
select {
case c <- payload:
default:
buffer.Release()
payload.Payload = nil
}
}
}
}
// Addr implements net.Listener.
func (h *Hub) Addr() net.Addr {
if h.conn == nil {
return h.connPacket.LocalAddr()
}
return h.conn.LocalAddr()
}