This commit is contained in:
RPRX 2020-11-25 19:01:53 +08:00
parent 47d23e9972
commit c7f7c08ead
711 changed files with 82154 additions and 2 deletions

13
transport/config.go Normal file
View file

@ -0,0 +1,13 @@
package transport
import (
"github.com/xtls/xray-core/v1/transport/internet"
)
// Apply applies this Config.
func (c *Config) Apply() error {
if c == nil {
return nil
}
return internet.ApplyGlobalTransportSettings(c.TransportSettings)
}

165
transport/config.pb.go Normal file
View file

@ -0,0 +1,165 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/config.proto
package transport
import (
proto "github.com/golang/protobuf/proto"
internet "github.com/xtls/xray-core/v1/transport/internet"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Global transport settings. This affects all type of connections that go
// through Xray. Deprecated. Use each settings in StreamConfig.
//
// Deprecated: Do not use.
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TransportSettings []*internet.TransportConfig `protobuf:"bytes,1,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_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_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_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetTransportSettings() []*internet.TransportConfig {
if x != nil {
return x.TransportSettings
}
return nil
}
var File_transport_config_proto protoreflect.FileDescriptor
var file_transport_config_proto_rawDesc = []byte{
0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x02, 0x18, 0x01,
0x42, 0x4f, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_config_proto_rawDescOnce sync.Once
file_transport_config_proto_rawDescData = file_transport_config_proto_rawDesc
)
func file_transport_config_proto_rawDescGZIP() []byte {
file_transport_config_proto_rawDescOnce.Do(func() {
file_transport_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_config_proto_rawDescData)
})
return file_transport_config_proto_rawDescData
}
var file_transport_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.Config
(*internet.TransportConfig)(nil), // 1: xray.transport.internet.TransportConfig
}
var file_transport_config_proto_depIdxs = []int32{
1, // 0: xray.transport.Config.transport_settings:type_name -> xray.transport.internet.TransportConfig
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_config_proto_init() }
func file_transport_config_proto_init() {
if File_transport_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_config_proto_goTypes,
DependencyIndexes: file_transport_config_proto_depIdxs,
MessageInfos: file_transport_config_proto_msgTypes,
}.Build()
File_transport_config_proto = out.File
file_transport_config_proto_rawDesc = nil
file_transport_config_proto_goTypes = nil
file_transport_config_proto_depIdxs = nil
}

16
transport/config.proto Normal file
View file

@ -0,0 +1,16 @@
syntax = "proto3";
package xray.transport;
option csharp_namespace = "Xray.Transport";
option go_package = "github.com/xtls/xray-core/v1/transport";
option java_package = "com.xray.transport";
option java_multiple_files = true;
import "transport/internet/config.proto";
// Global transport settings. This affects all type of connections that go
// through Xray. Deprecated. Use each settings in StreamConfig.
message Config {
option deprecated = true;
repeated xray.transport.internet.TransportConfig transport_settings = 1;
}

View file

@ -0,0 +1,124 @@
package internet
import (
"github.com/xtls/xray-core/v1/common/serial"
"github.com/xtls/xray-core/v1/features"
)
type ConfigCreator func() interface{}
var (
globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
globalTransportSettings []*TransportConfig
)
const unknownProtocol = "unknown"
func transportProtocolToString(protocol TransportProtocol) string {
switch protocol {
case TransportProtocol_TCP:
return "tcp"
case TransportProtocol_UDP:
return "udp"
case TransportProtocol_HTTP:
return "http"
case TransportProtocol_MKCP:
return "mkcp"
case TransportProtocol_WebSocket:
return "websocket"
case TransportProtocol_DomainSocket:
return "domainsocket"
default:
return unknownProtocol
}
}
func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
if _, found := globalTransportConfigCreatorCache[name]; found {
return newError("protocol ", name, " is already registered").AtError()
}
globalTransportConfigCreatorCache[name] = creator
return nil
}
func CreateTransportConfig(name string) (interface{}, error) {
creator, ok := globalTransportConfigCreatorCache[name]
if !ok {
return nil, newError("unknown transport protocol: ", name)
}
return creator(), nil
}
func (c *TransportConfig) GetTypedSettings() (interface{}, error) {
return c.Settings.GetInstance()
}
func (c *TransportConfig) GetUnifiedProtocolName() string {
if len(c.ProtocolName) > 0 {
return c.ProtocolName
}
return transportProtocolToString(c.Protocol)
}
func (c *StreamConfig) GetEffectiveProtocol() string {
if c == nil {
return "tcp"
}
if len(c.ProtocolName) > 0 {
return c.ProtocolName
}
return transportProtocolToString(c.Protocol)
}
func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) {
protocol := c.GetEffectiveProtocol()
return c.GetTransportSettingsFor(protocol)
}
func (c *StreamConfig) GetTransportSettingsFor(protocol string) (interface{}, error) {
if c != nil {
for _, settings := range c.TransportSettings {
if settings.GetUnifiedProtocolName() == protocol {
return settings.GetTypedSettings()
}
}
}
for _, settings := range globalTransportSettings {
if settings.GetUnifiedProtocolName() == protocol {
return settings.GetTypedSettings()
}
}
return CreateTransportConfig(protocol)
}
func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
for _, settings := range c.SecuritySettings {
if settings.Type == c.SecurityType {
return settings.GetInstance()
}
}
return serial.GetInstance(c.SecurityType)
}
func (c *StreamConfig) HasSecuritySettings() bool {
return len(c.SecurityType) > 0
}
func ApplyGlobalTransportSettings(settings []*TransportConfig) error {
features.PrintDeprecatedFeatureWarning("global transport settings")
globalTransportSettings = settings
return nil
}
func (c *ProxyConfig) HasTag() bool {
return c != nil && len(c.Tag) > 0
}
func (m SocketConfig_TProxyMode) IsEnabled() bool {
return m != SocketConfig_Off
}

View file

@ -0,0 +1,711 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/config.proto
package internet
import (
proto "github.com/golang/protobuf/proto"
serial "github.com/xtls/xray-core/v1/common/serial"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type TransportProtocol int32
const (
TransportProtocol_TCP TransportProtocol = 0
TransportProtocol_UDP TransportProtocol = 1
TransportProtocol_MKCP TransportProtocol = 2
TransportProtocol_WebSocket TransportProtocol = 3
TransportProtocol_HTTP TransportProtocol = 4
TransportProtocol_DomainSocket TransportProtocol = 5
)
// Enum value maps for TransportProtocol.
var (
TransportProtocol_name = map[int32]string{
0: "TCP",
1: "UDP",
2: "MKCP",
3: "WebSocket",
4: "HTTP",
5: "DomainSocket",
}
TransportProtocol_value = map[string]int32{
"TCP": 0,
"UDP": 1,
"MKCP": 2,
"WebSocket": 3,
"HTTP": 4,
"DomainSocket": 5,
}
)
func (x TransportProtocol) Enum() *TransportProtocol {
p := new(TransportProtocol)
*p = x
return p
}
func (x TransportProtocol) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (TransportProtocol) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_config_proto_enumTypes[0].Descriptor()
}
func (TransportProtocol) Type() protoreflect.EnumType {
return &file_transport_internet_config_proto_enumTypes[0]
}
func (x TransportProtocol) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use TransportProtocol.Descriptor instead.
func (TransportProtocol) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
}
type SocketConfig_TCPFastOpenState int32
const (
// AsIs is to leave the current TFO state as is, unmodified.
SocketConfig_AsIs SocketConfig_TCPFastOpenState = 0
// Enable is for enabling TFO explictly.
SocketConfig_Enable SocketConfig_TCPFastOpenState = 1
// Disable is for disabling TFO explictly.
SocketConfig_Disable SocketConfig_TCPFastOpenState = 2
)
// Enum value maps for SocketConfig_TCPFastOpenState.
var (
SocketConfig_TCPFastOpenState_name = map[int32]string{
0: "AsIs",
1: "Enable",
2: "Disable",
}
SocketConfig_TCPFastOpenState_value = map[string]int32{
"AsIs": 0,
"Enable": 1,
"Disable": 2,
}
)
func (x SocketConfig_TCPFastOpenState) Enum() *SocketConfig_TCPFastOpenState {
p := new(SocketConfig_TCPFastOpenState)
*p = x
return p
}
func (x SocketConfig_TCPFastOpenState) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (SocketConfig_TCPFastOpenState) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
}
func (SocketConfig_TCPFastOpenState) Type() protoreflect.EnumType {
return &file_transport_internet_config_proto_enumTypes[1]
}
func (x SocketConfig_TCPFastOpenState) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use SocketConfig_TCPFastOpenState.Descriptor instead.
func (SocketConfig_TCPFastOpenState) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 0}
}
type SocketConfig_TProxyMode int32
const (
// TProxy is off.
SocketConfig_Off SocketConfig_TProxyMode = 0
// TProxy mode.
SocketConfig_TProxy SocketConfig_TProxyMode = 1
// Redirect mode.
SocketConfig_Redirect SocketConfig_TProxyMode = 2
)
// Enum value maps for SocketConfig_TProxyMode.
var (
SocketConfig_TProxyMode_name = map[int32]string{
0: "Off",
1: "TProxy",
2: "Redirect",
}
SocketConfig_TProxyMode_value = map[string]int32{
"Off": 0,
"TProxy": 1,
"Redirect": 2,
}
)
func (x SocketConfig_TProxyMode) Enum() *SocketConfig_TProxyMode {
p := new(SocketConfig_TProxyMode)
*p = x
return p
}
func (x SocketConfig_TProxyMode) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_config_proto_enumTypes[2].Descriptor()
}
func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
return &file_transport_internet_config_proto_enumTypes[2]
}
func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 1}
}
type TransportConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Type of network that this settings supports.
// Deprecated. Use the string form below.
//
// Deprecated: Do not use.
Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=xray.transport.internet.TransportProtocol" json:"protocol,omitempty"`
// Type of network that this settings supports.
ProtocolName string `protobuf:"bytes,3,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"`
// Specific settings. Must be of the transports.
Settings *serial.TypedMessage `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"`
}
func (x *TransportConfig) Reset() {
*x = TransportConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TransportConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransportConfig) ProtoMessage() {}
func (x *TransportConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_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 TransportConfig.ProtoReflect.Descriptor instead.
func (*TransportConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
}
// Deprecated: Do not use.
func (x *TransportConfig) GetProtocol() TransportProtocol {
if x != nil {
return x.Protocol
}
return TransportProtocol_TCP
}
func (x *TransportConfig) GetProtocolName() string {
if x != nil {
return x.ProtocolName
}
return ""
}
func (x *TransportConfig) GetSettings() *serial.TypedMessage {
if x != nil {
return x.Settings
}
return nil
}
type StreamConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Effective network. Deprecated. Use the string form below.
//
// Deprecated: Do not use.
Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=xray.transport.internet.TransportProtocol" json:"protocol,omitempty"`
// Effective network.
ProtocolName string `protobuf:"bytes,5,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"`
TransportSettings []*TransportConfig `protobuf:"bytes,2,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"`
// Type of security. Must be a message name of the settings proto.
SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType,proto3" json:"security_type,omitempty"`
// Settings for transport security. For now the only choice is TLS.
SecuritySettings []*serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"`
SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"`
}
func (x *StreamConfig) Reset() {
*x = StreamConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *StreamConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StreamConfig) ProtoMessage() {}
func (x *StreamConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_config_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 StreamConfig.ProtoReflect.Descriptor instead.
func (*StreamConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
}
// Deprecated: Do not use.
func (x *StreamConfig) GetProtocol() TransportProtocol {
if x != nil {
return x.Protocol
}
return TransportProtocol_TCP
}
func (x *StreamConfig) GetProtocolName() string {
if x != nil {
return x.ProtocolName
}
return ""
}
func (x *StreamConfig) GetTransportSettings() []*TransportConfig {
if x != nil {
return x.TransportSettings
}
return nil
}
func (x *StreamConfig) GetSecurityType() string {
if x != nil {
return x.SecurityType
}
return ""
}
func (x *StreamConfig) GetSecuritySettings() []*serial.TypedMessage {
if x != nil {
return x.SecuritySettings
}
return nil
}
func (x *StreamConfig) GetSocketSettings() *SocketConfig {
if x != nil {
return x.SocketSettings
}
return nil
}
type ProxyConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
}
func (x *ProxyConfig) Reset() {
*x = ProxyConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ProxyConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ProxyConfig) ProtoMessage() {}
func (x *ProxyConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_config_proto_msgTypes[2]
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 ProxyConfig.ProtoReflect.Descriptor instead.
func (*ProxyConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
}
func (x *ProxyConfig) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
// SocketConfig is options to be applied on network sockets.
type SocketConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Mark of the connection. If non-zero, the value will be set to SO_MARK.
Mark int32 `protobuf:"varint,1,opt,name=mark,proto3" json:"mark,omitempty"`
// TFO is the state of TFO settings.
Tfo SocketConfig_TCPFastOpenState `protobuf:"varint,2,opt,name=tfo,proto3,enum=xray.transport.internet.SocketConfig_TCPFastOpenState" json:"tfo,omitempty"`
// TProxy is for enabling TProxy socket option.
Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
// option. This option is for UDP only.
ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
}
func (x *SocketConfig) Reset() {
*x = SocketConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SocketConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SocketConfig) ProtoMessage() {}
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_config_proto_msgTypes[3]
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 SocketConfig.ProtoReflect.Descriptor instead.
func (*SocketConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
}
func (x *SocketConfig) GetMark() int32 {
if x != nil {
return x.Mark
}
return 0
}
func (x *SocketConfig) GetTfo() SocketConfig_TCPFastOpenState {
if x != nil {
return x.Tfo
}
return SocketConfig_AsIs
}
func (x *SocketConfig) GetTproxy() SocketConfig_TProxyMode {
if x != nil {
return x.Tproxy
}
return SocketConfig_Off
}
func (x *SocketConfig) GetReceiveOriginalDestAddress() bool {
if x != nil {
return x.ReceiveOriginalDestAddress
}
return false
}
func (x *SocketConfig) GetBindAddress() []byte {
if x != nil {
return x.BindAddress
}
return nil
}
func (x *SocketConfig) GetBindPort() uint32 {
if x != nil {
return x.BindPort
}
return 0
}
func (x *SocketConfig) GetAcceptProxyProtocol() bool {
if x != nil {
return x.AcceptProxyProtocol
}
return false
}
var File_transport_internet_config_proto protoreflect.FileDescriptor
var file_transport_internet_config_proto_rawDesc = []byte{
0x0a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x01,
0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42,
0x02, 0x18, 0x01, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x23, 0x0a,
0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x22, 0x9c, 0x03, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42,
0x02, 0x18, 0x01, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x23, 0x0a,
0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f,
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73,
0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65,
0x12, 0x4d, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c,
0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x73,
0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x4e, 0x0a, 0x0f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22,
0x1f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67,
0x22, 0xd5, 0x03, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x48, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63,
0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x43, 0x50, 0x46, 0x61, 0x73,
0x74, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12,
0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64,
0x65, 0x52, 0x06, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63,
0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65,
0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
0x6c, 0x44, 0x65, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c,
0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15,
0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63,
0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x22, 0x35, 0x0a, 0x10, 0x54, 0x43, 0x50, 0x46, 0x61, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x6e, 0x53,
0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x0a,
0x0a, 0x06, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x69,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x02, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a,
0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65,
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a,
0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12,
0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62,
0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50,
0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b,
0x65, 0x74, 0x10, 0x05, 0x42, 0x6a, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_config_proto_rawDescOnce sync.Once
file_transport_internet_config_proto_rawDescData = file_transport_internet_config_proto_rawDesc
)
func file_transport_internet_config_proto_rawDescGZIP() []byte {
file_transport_internet_config_proto_rawDescOnce.Do(func() {
file_transport_internet_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_config_proto_rawDescData)
})
return file_transport_internet_config_proto_rawDescData
}
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_transport_internet_config_proto_goTypes = []interface{}{
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
(SocketConfig_TCPFastOpenState)(0), // 1: xray.transport.internet.SocketConfig.TCPFastOpenState
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
(*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig
(*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage
}
var file_transport_internet_config_proto_depIdxs = []int32{
0, // 0: xray.transport.internet.TransportConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
7, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
0, // 2: xray.transport.internet.StreamConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
3, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
7, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
6, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
1, // 6: xray.transport.internet.SocketConfig.tfo:type_name -> xray.transport.internet.SocketConfig.TCPFastOpenState
2, // 7: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_transport_internet_config_proto_init() }
func file_transport_internet_config_proto_init() {
if File_transport_internet_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TransportConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StreamConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProxyConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SocketConfig); 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_config_proto_rawDesc,
NumEnums: 3,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_config_proto_goTypes,
DependencyIndexes: file_transport_internet_config_proto_depIdxs,
EnumInfos: file_transport_internet_config_proto_enumTypes,
MessageInfos: file_transport_internet_config_proto_msgTypes,
}.Build()
File_transport_internet_config_proto = out.File
file_transport_internet_config_proto_rawDesc = nil
file_transport_internet_config_proto_goTypes = nil
file_transport_internet_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,90 @@
syntax = "proto3";
package xray.transport.internet;
option csharp_namespace = "Xray.Transport.Internet";
option go_package = "github.com/xtls/xray-core/v1/transport/internet";
option java_package = "com.xray.transport.internet";
option java_multiple_files = true;
import "common/serial/typed_message.proto";
enum TransportProtocol {
TCP = 0;
UDP = 1;
MKCP = 2;
WebSocket = 3;
HTTP = 4;
DomainSocket = 5;
}
message TransportConfig {
// Type of network that this settings supports.
// Deprecated. Use the string form below.
TransportProtocol protocol = 1 [ deprecated = true ];
// Type of network that this settings supports.
string protocol_name = 3;
// Specific settings. Must be of the transports.
xray.common.serial.TypedMessage settings = 2;
}
message StreamConfig {
// Effective network. Deprecated. Use the string form below.
TransportProtocol protocol = 1 [ deprecated = true ];
// Effective network.
string protocol_name = 5;
repeated TransportConfig transport_settings = 2;
// Type of security. Must be a message name of the settings proto.
string security_type = 3;
// Settings for transport security. For now the only choice is TLS.
repeated xray.common.serial.TypedMessage security_settings = 4;
SocketConfig socket_settings = 6;
}
message ProxyConfig { string tag = 1; }
// SocketConfig is options to be applied on network sockets.
message SocketConfig {
// Mark of the connection. If non-zero, the value will be set to SO_MARK.
int32 mark = 1;
enum TCPFastOpenState {
// AsIs is to leave the current TFO state as is, unmodified.
AsIs = 0;
// Enable is for enabling TFO explictly.
Enable = 1;
// Disable is for disabling TFO explictly.
Disable = 2;
}
// TFO is the state of TFO settings.
TCPFastOpenState tfo = 2;
enum TProxyMode {
// TProxy is off.
Off = 0;
// TProxy mode.
TProxy = 1;
// Redirect mode.
Redirect = 2;
}
// TProxy is for enabling TProxy socket option.
TProxyMode tproxy = 3;
// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
// option. This option is for UDP only.
bool receive_original_dest_address = 4;
bytes bind_address = 5;
uint32 bind_port = 6;
bool accept_proxy_protocol = 7;
}

View file

@ -0,0 +1,34 @@
package internet
import (
"net"
"github.com/xtls/xray-core/v1/features/stats"
)
type Connection interface {
net.Conn
}
type StatCouterConnection struct {
Connection
ReadCounter stats.Counter
WriteCounter stats.Counter
}
func (c *StatCouterConnection) Read(b []byte) (int, error) {
nBytes, err := c.Connection.Read(b)
if c.ReadCounter != nil {
c.ReadCounter.Add(int64(nBytes))
}
return nBytes, err
}
func (c *StatCouterConnection) Write(b []byte) (int, error) {
nBytes, err := c.Connection.Write(b)
if c.WriteCounter != nil {
c.WriteCounter.Add(int64(nBytes))
}
return nBytes, err
}

View file

@ -0,0 +1,72 @@
package internet
import (
"context"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/session"
)
// Dialer is the interface for dialing outbound connections.
type Dialer interface {
// Dial dials a system connection to the given destination.
Dial(ctx context.Context, destination net.Destination) (Connection, error)
// Address returns the address used by this Dialer. Maybe nil if not known.
Address() net.Address
}
// dialFunc is an interface to dial network connection to a specific destination.
type dialFunc func(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error)
var (
transportDialerCache = make(map[string]dialFunc)
)
// RegisterTransportDialer registers a Dialer with given name.
func RegisterTransportDialer(protocol string, dialer dialFunc) error {
if _, found := transportDialerCache[protocol]; found {
return newError(protocol, " dialer already registered").AtError()
}
transportDialerCache[protocol] = dialer
return nil
}
// Dial dials a internet connection towards the given destination.
func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error) {
if dest.Network == net.Network_TCP {
if streamSettings == nil {
s, err := ToMemoryStreamConfig(nil)
if err != nil {
return nil, newError("failed to create default stream settings").Base(err)
}
streamSettings = s
}
protocol := streamSettings.ProtocolName
dialer := transportDialerCache[protocol]
if dialer == nil {
return nil, newError(protocol, " dialer not registered").AtError()
}
return dialer(ctx, dest, streamSettings)
}
if dest.Network == net.Network_UDP {
udpDialer := transportDialerCache["udp"]
if udpDialer == nil {
return nil, newError("UDP dialer not registered").AtError()
}
return udpDialer(ctx, dest, streamSettings)
}
return nil, newError("unknown network ", dest.Network)
}
// DialSystem calls system dialer to create a network connection.
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
var src net.Address
if outbound := session.OutboundFromContext(ctx); outbound != nil {
src = outbound.Gateway
}
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
}

View file

@ -0,0 +1,27 @@
package internet_test
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/testing/servers/tcp"
. "github.com/xtls/xray-core/v1/transport/internet"
)
func TestDialWithLocalAddr(t *testing.T) {
server := &tcp.Server{}
dest, err := server.Start()
common.Must(err)
defer server.Close()
conn, err := DialSystem(context.Background(), net.TCPDestination(net.LocalHostIP, dest.Port), nil)
common.Must(err)
if r := cmp.Diff(conn.RemoteAddr().String(), "127.0.0.1:"+dest.Port.String()); r != "" {
t.Error(r)
}
conn.Close()
}

View file

@ -0,0 +1,38 @@
// +build !confonly
package domainsocket
import (
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
)
const protocolName = "domainsocket"
const sizeofSunPath = 108
func (c *Config) GetUnixAddr() (*net.UnixAddr, error) {
path := c.Path
if path == "" {
return nil, newError("empty domain socket path")
}
if c.Abstract && path[0] != '@' {
path = "@" + path
}
if c.Abstract && c.Padding {
raw := []byte(path)
addr := make([]byte, sizeofSunPath)
copy(addr, raw)
path = string(addr)
}
return &net.UnixAddr{
Name: path,
Net: "unix",
}, nil
}
func init() {
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
return new(Config)
}))
}

View file

@ -0,0 +1,185 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/domainsocket/config.proto
package domainsocket
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Path of the domain socket. This overrides the IP/Port parameter from
// upstream caller.
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
// Abstract speicifies whether to use abstract namespace or not.
// Traditionally Unix domain socket is file system based. Abstract domain
// socket can be used without acquiring file lock.
Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"`
// Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
// connect(2) or bind(2) when using abstract UDS.
Padding bool `protobuf:"varint,3,opt,name=padding,proto3" json:"padding,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_domainsocket_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_domainsocket_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_domainsocket_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
func (x *Config) GetAbstract() bool {
if x != nil {
return x.Abstract
}
return false
}
func (x *Config) GetPadding() bool {
if x != nil {
return x.Padding
}
return false
}
var File_transport_internet_domainsocket_config_proto protoreflect.FileDescriptor
var file_transport_internet_domainsocket_config_proto_rawDesc = []byte{
0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x22, 0x52, 0x0a, 0x06, 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, 0x1a, 0x0a, 0x08, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x18,
0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x91, 0x01, 0x0a, 0x28, 0x63, 0x6f, 0x6d,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
0x6f, 0x63, 0x6b, 0x65, 0x74, 0xaa, 0x02, 0x24, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_domainsocket_config_proto_rawDescOnce sync.Once
file_transport_internet_domainsocket_config_proto_rawDescData = file_transport_internet_domainsocket_config_proto_rawDesc
)
func file_transport_internet_domainsocket_config_proto_rawDescGZIP() []byte {
file_transport_internet_domainsocket_config_proto_rawDescOnce.Do(func() {
file_transport_internet_domainsocket_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_domainsocket_config_proto_rawDescData)
})
return file_transport_internet_domainsocket_config_proto_rawDescData
}
var file_transport_internet_domainsocket_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_domainsocket_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.domainsocket.Config
}
var file_transport_internet_domainsocket_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_domainsocket_config_proto_init() }
func file_transport_internet_domainsocket_config_proto_init() {
if File_transport_internet_domainsocket_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_domainsocket_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_domainsocket_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_domainsocket_config_proto_goTypes,
DependencyIndexes: file_transport_internet_domainsocket_config_proto_depIdxs,
MessageInfos: file_transport_internet_domainsocket_config_proto_msgTypes,
}.Build()
File_transport_internet_domainsocket_config_proto = out.File
file_transport_internet_domainsocket_config_proto_rawDesc = nil
file_transport_internet_domainsocket_config_proto_goTypes = nil
file_transport_internet_domainsocket_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,20 @@
syntax = "proto3";
package xray.transport.internet.domainsocket;
option csharp_namespace = "Xray.Transport.Internet.DomainSocket";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/domainsocket";
option java_package = "com.xray.transport.internet.domainsocket";
option java_multiple_files = true;
message Config {
// Path of the domain socket. This overrides the IP/Port parameter from
// upstream caller.
string path = 1;
// Abstract speicifies whether to use abstract namespace or not.
// Traditionally Unix domain socket is file system based. Abstract domain
// socket can be used without acquiring file lock.
bool abstract = 2;
// Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
// connect(2) or bind(2) when using abstract UDS.
bool padding = 3;
}

View file

@ -0,0 +1,40 @@
// +build !windows
// +build !wasm
// +build !confonly
package domainsocket
import (
"context"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
"github.com/xtls/xray-core/v1/transport/internet/xtls"
)
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
settings := streamSettings.ProtocolSettings.(*Config)
addr, err := settings.GetUnixAddr()
if err != nil {
return nil, err
}
conn, err := net.DialUnix("unix", nil, addr)
if err != nil {
return nil, newError("failed to dial unix: ", settings.Path).Base(err).AtWarning()
}
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
return tls.Client(conn, config.GetTLSConfig(tls.WithDestination(dest))), nil
} else if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
return xtls.Client(conn, config.GetXTLSConfig(xtls.WithDestination(dest))), nil
}
return conn, nil
}
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
}

View file

@ -0,0 +1,3 @@
package domainsocket
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen

View file

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

View file

@ -0,0 +1,138 @@
// +build !windows
// +build !wasm
// +build !confonly
package domainsocket
import (
"context"
gotls "crypto/tls"
"os"
"strings"
goxtls "github.com/xtls/go"
"golang.org/x/sys/unix"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
"github.com/xtls/xray-core/v1/transport/internet/xtls"
)
type Listener struct {
addr *net.UnixAddr
ln net.Listener
tlsConfig *gotls.Config
xtlsConfig *goxtls.Config
config *Config
addConn internet.ConnHandler
locker *fileLocker
}
func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
settings := streamSettings.ProtocolSettings.(*Config)
addr, err := settings.GetUnixAddr()
if err != nil {
return nil, err
}
unixListener, err := net.ListenUnix("unix", addr)
if err != nil {
return nil, newError("failed to listen domain socket").Base(err).AtWarning()
}
ln := &Listener{
addr: addr,
ln: unixListener,
config: settings,
addConn: handler,
}
if !settings.Abstract {
ln.locker = &fileLocker{
path: settings.Path + ".lock",
}
if err := ln.locker.Acquire(); err != nil {
unixListener.Close()
return nil, err
}
}
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
ln.tlsConfig = config.GetTLSConfig()
}
if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
ln.xtlsConfig = config.GetXTLSConfig()
}
go ln.run()
return ln, nil
}
func (ln *Listener) Addr() net.Addr {
return ln.addr
}
func (ln *Listener) Close() error {
if ln.locker != nil {
ln.locker.Release()
}
return ln.ln.Close()
}
func (ln *Listener) run() {
for {
conn, err := ln.ln.Accept()
if err != nil {
if strings.Contains(err.Error(), "closed") {
break
}
newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog()
continue
}
if ln.tlsConfig != nil {
conn = tls.Server(conn, ln.tlsConfig)
} else if ln.xtlsConfig != nil {
conn = xtls.Server(conn, ln.xtlsConfig)
}
ln.addConn(internet.Connection(conn))
}
}
type fileLocker struct {
path string
file *os.File
}
func (fl *fileLocker) Acquire() error {
f, err := os.Create(fl.path)
if err != nil {
return err
}
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
f.Close()
return newError("failed to lock file: ", fl.path).Base(err)
}
fl.file = f
return nil
}
func (fl *fileLocker) Release() {
if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil {
newError("failed to unlock file: ", fl.path).Base(err).WriteToLog()
}
if err := fl.file.Close(); err != nil {
newError("failed to close file: ", fl.path).Base(err).WriteToLog()
}
if err := os.Remove(fl.path); err != nil {
newError("failed to remove file: ", fl.path).Base(err).WriteToLog()
}
}
func init() {
common.Must(internet.RegisterTransportListener(protocolName, Listen))
}

View file

@ -0,0 +1,92 @@
// +build !windows
package domainsocket_test
import (
"context"
"runtime"
"testing"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
. "github.com/xtls/xray-core/v1/transport/internet/domainsocket"
)
func TestListen(t *testing.T) {
ctx := context.Background()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "domainsocket",
ProtocolSettings: &Config{
Path: "/tmp/ts3",
},
}
listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn internet.Connection) {
defer conn.Close()
b := buf.New()
defer b.Release()
common.Must2(b.ReadFrom(conn))
b.WriteString("Response")
common.Must2(conn.Write(b.Bytes()))
})
common.Must(err)
defer listener.Close()
conn, err := Dial(ctx, net.Destination{}, streamSettings)
common.Must(err)
defer conn.Close()
common.Must2(conn.Write([]byte("Request")))
b := buf.New()
defer b.Release()
common.Must2(b.ReadFrom(conn))
if b.String() != "RequestResponse" {
t.Error("expected response as 'RequestResponse' but got ", b.String())
}
}
func TestListenAbstract(t *testing.T) {
if runtime.GOOS != "linux" {
return
}
ctx := context.Background()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "domainsocket",
ProtocolSettings: &Config{
Path: "/tmp/ts3",
Abstract: true,
},
}
listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn internet.Connection) {
defer conn.Close()
b := buf.New()
defer b.Release()
common.Must2(b.ReadFrom(conn))
b.WriteString("Response")
common.Must2(conn.Write(b.Bytes()))
})
common.Must(err)
defer listener.Close()
conn, err := Dial(ctx, net.Destination{}, streamSettings)
common.Must(err)
defer conn.Close()
common.Must2(conn.Write([]byte("Request")))
b := buf.New()
defer b.Release()
common.Must2(b.ReadFrom(conn))
if b.String() != "RequestResponse" {
t.Error("expected response as 'RequestResponse' but got ", b.String())
}
}

View file

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

View file

@ -0,0 +1,11 @@
package internet
import (
"os"
)
// FileLocker is UDS access lock
type FileLocker struct {
path string
file *os.File
}

View file

@ -0,0 +1,36 @@
// +build !windows
package internet
import (
"os"
"golang.org/x/sys/unix"
)
// Acquire lock
func (fl *FileLocker) Acquire() error {
f, err := os.Create(fl.path)
if err != nil {
return err
}
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
f.Close()
return newError("failed to lock file: ", fl.path).Base(err)
}
fl.file = f
return nil
}
// Release lock
func (fl *FileLocker) Release() {
if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil {
newError("failed to unlock file: ", fl.path).Base(err).WriteToLog()
}
if err := fl.file.Close(); err != nil {
newError("failed to close file: ", fl.path).Base(err).WriteToLog()
}
if err := os.Remove(fl.path); err != nil {
newError("failed to remove file: ", fl.path).Base(err).WriteToLog()
}
}

View file

@ -0,0 +1,11 @@
package internet
// Acquire lock
func (fl *FileLocker) Acquire() error {
return nil
}
// Release lock
func (fl *FileLocker) Release() {
return
}

View file

@ -0,0 +1,40 @@
package internet
import (
"context"
"net"
"github.com/xtls/xray-core/v1/common"
)
type PacketHeader interface {
Size() int32
Serialize([]byte)
}
func CreatePacketHeader(config interface{}) (PacketHeader, error) {
header, err := common.CreateObject(context.Background(), config)
if err != nil {
return nil, err
}
if h, ok := header.(PacketHeader); ok {
return h, nil
}
return nil, newError("not a packet header")
}
type ConnectionAuthenticator interface {
Client(net.Conn) net.Conn
Server(net.Conn) net.Conn
}
func CreateConnectionAuthenticator(config interface{}) (ConnectionAuthenticator, error) {
auth, err := common.CreateObject(context.Background(), config)
if err != nil {
return nil, err
}
if a, ok := auth.(ConnectionAuthenticator); ok {
return a, nil
}
return nil, newError("not a ConnectionAuthenticator")
}

View file

@ -0,0 +1,49 @@
package internet_test
import (
"testing"
"github.com/xtls/xray-core/v1/common"
. "github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/headers/noop"
"github.com/xtls/xray-core/v1/transport/internet/headers/srtp"
"github.com/xtls/xray-core/v1/transport/internet/headers/utp"
"github.com/xtls/xray-core/v1/transport/internet/headers/wechat"
"github.com/xtls/xray-core/v1/transport/internet/headers/wireguard"
)
func TestAllHeadersLoadable(t *testing.T) {
testCases := []struct {
Input interface{}
Size int32
}{
{
Input: new(noop.Config),
Size: 0,
},
{
Input: new(srtp.Config),
Size: 4,
},
{
Input: new(utp.Config),
Size: 4,
},
{
Input: new(wechat.VideoConfig),
Size: 13,
},
{
Input: new(wireguard.WireguardConfig),
Size: 4,
},
}
for _, testCase := range testCases {
header, err := CreatePacketHeader(testCase.Input)
common.Must(err)
if header.Size() != testCase.Size {
t.Error("expected size ", testCase.Size, " but got ", header.Size())
}
}
}

View file

@ -0,0 +1,100 @@
package http
import (
"strings"
"github.com/xtls/xray-core/v1/common/dice"
)
func pickString(arr []string) string {
n := len(arr)
switch n {
case 0:
return ""
case 1:
return arr[0]
default:
return arr[dice.Roll(n)]
}
}
func (v *RequestConfig) PickURI() string {
return pickString(v.Uri)
}
func (v *RequestConfig) PickHeaders() []string {
n := len(v.Header)
if n == 0 {
return nil
}
headers := make([]string, n)
for idx, headerConfig := range v.Header {
headerName := headerConfig.Name
headerValue := pickString(headerConfig.Value)
headers[idx] = headerName + ": " + headerValue
}
return headers
}
func (v *RequestConfig) GetVersionValue() string {
if v == nil || v.Version == nil {
return "1.1"
}
return v.Version.Value
}
func (v *RequestConfig) GetMethodValue() string {
if v == nil || v.Method == nil {
return "GET"
}
return v.Method.Value
}
func (v *RequestConfig) GetFullVersion() string {
return "HTTP/" + v.GetVersionValue()
}
func (v *ResponseConfig) HasHeader(header string) bool {
cHeader := strings.ToLower(header)
for _, tHeader := range v.Header {
if strings.EqualFold(tHeader.Name, cHeader) {
return true
}
}
return false
}
func (v *ResponseConfig) PickHeaders() []string {
n := len(v.Header)
if n == 0 {
return nil
}
headers := make([]string, n)
for idx, headerConfig := range v.Header {
headerName := headerConfig.Name
headerValue := pickString(headerConfig.Value)
headers[idx] = headerName + ": " + headerValue
}
return headers
}
func (v *ResponseConfig) GetVersionValue() string {
if v == nil || v.Version == nil {
return "1.1"
}
return v.Version.Value
}
func (v *ResponseConfig) GetFullVersion() string {
return "HTTP/" + v.GetVersionValue()
}
func (v *ResponseConfig) GetStatusValue() *Status {
if v == nil || v.Status == nil {
return &Status{
Code: "200",
Reason: "OK",
}
}
return v.Status
}

View file

@ -0,0 +1,654 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/http/config.proto
package http
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Header struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// "Accept", "Cookie", etc
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Each entry must be valid in one piece. Random entry will be chosen if
// multiple entries present.
Value []string `protobuf:"bytes,2,rep,name=value,proto3" json:"value,omitempty"`
}
func (x *Header) Reset() {
*x = Header{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Header) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Header) ProtoMessage() {}
func (x *Header) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_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 Header.ProtoReflect.Descriptor instead.
func (*Header) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{0}
}
func (x *Header) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Header) GetValue() []string {
if x != nil {
return x.Value
}
return nil
}
// HTTP version. Default value "1.1".
type Version struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *Version) Reset() {
*x = Version{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Version) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Version) ProtoMessage() {}
func (x *Version) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_config_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 Version.ProtoReflect.Descriptor instead.
func (*Version) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{1}
}
func (x *Version) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
// HTTP method. Default value "GET".
type Method struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *Method) Reset() {
*x = Method{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Method) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Method) ProtoMessage() {}
func (x *Method) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[2]
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 Method.ProtoReflect.Descriptor instead.
func (*Method) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{2}
}
func (x *Method) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type RequestConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Full HTTP version like "1.1".
Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
// GET, POST, CONNECT etc
Method *Method `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
// URI like "/login.php"
Uri []string `protobuf:"bytes,3,rep,name=uri,proto3" json:"uri,omitempty"`
Header []*Header `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty"`
}
func (x *RequestConfig) Reset() {
*x = RequestConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RequestConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RequestConfig) ProtoMessage() {}
func (x *RequestConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[3]
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 RequestConfig.ProtoReflect.Descriptor instead.
func (*RequestConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{3}
}
func (x *RequestConfig) GetVersion() *Version {
if x != nil {
return x.Version
}
return nil
}
func (x *RequestConfig) GetMethod() *Method {
if x != nil {
return x.Method
}
return nil
}
func (x *RequestConfig) GetUri() []string {
if x != nil {
return x.Uri
}
return nil
}
func (x *RequestConfig) GetHeader() []*Header {
if x != nil {
return x.Header
}
return nil
}
type Status struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Status code. Default "200".
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
// Statue reason. Default "OK".
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
}
func (x *Status) Reset() {
*x = Status{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Status) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Status) ProtoMessage() {}
func (x *Status) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[4]
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 Status.ProtoReflect.Descriptor instead.
func (*Status) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{4}
}
func (x *Status) GetCode() string {
if x != nil {
return x.Code
}
return ""
}
func (x *Status) GetReason() string {
if x != nil {
return x.Reason
}
return ""
}
type ResponseConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Status *Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
Header []*Header `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"`
}
func (x *ResponseConfig) Reset() {
*x = ResponseConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ResponseConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResponseConfig) ProtoMessage() {}
func (x *ResponseConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[5]
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 ResponseConfig.ProtoReflect.Descriptor instead.
func (*ResponseConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{5}
}
func (x *ResponseConfig) GetVersion() *Version {
if x != nil {
return x.Version
}
return nil
}
func (x *ResponseConfig) GetStatus() *Status {
if x != nil {
return x.Status
}
return nil
}
func (x *ResponseConfig) GetHeader() []*Header {
if x != nil {
return x.Header
}
return nil
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Settings for authenticating requests. If not set, client side will not send
// authenication header, and server side will bypass authentication.
Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"`
// Settings for authenticating responses. If not set, client side will bypass
// authentication, and server side will not send authentication header.
Response *ResponseConfig `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_http_config_proto_msgTypes[6]
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_headers_http_config_proto_msgTypes[6]
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_headers_http_config_proto_rawDescGZIP(), []int{6}
}
func (x *Config) GetRequest() *RequestConfig {
if x != nil {
return x.Request
}
return nil
}
func (x *Config) GetResponse() *ResponseConfig {
if x != nil {
return x.Response
}
return nil
}
var File_transport_internet_headers_http_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_http_config_proto_rawDesc = []byte{
0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x68, 0x74, 0x74,
0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
0x68, 0x74, 0x74, 0x70, 0x22, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28,
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, 0x06, 0x4d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x0d, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
0x69, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x44, 0x0a, 0x06,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64,
0x65, 0x72, 0x22, 0x34, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04,
0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x06, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74,
0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x22, 0xa9, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4d, 0x0a, 0x07, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x91, 0x01, 0x0a,
0x28, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x24, 0x58, 0x72, 0x61, 0x79,
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x48, 0x74, 0x74, 0x70,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_http_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_http_config_proto_rawDescData = file_transport_internet_headers_http_config_proto_rawDesc
)
func file_transport_internet_headers_http_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_http_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_http_config_proto_rawDescData)
})
return file_transport_internet_headers_http_config_proto_rawDescData
}
var file_transport_internet_headers_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_transport_internet_headers_http_config_proto_goTypes = []interface{}{
(*Header)(nil), // 0: xray.transport.internet.headers.http.Header
(*Version)(nil), // 1: xray.transport.internet.headers.http.Version
(*Method)(nil), // 2: xray.transport.internet.headers.http.Method
(*RequestConfig)(nil), // 3: xray.transport.internet.headers.http.RequestConfig
(*Status)(nil), // 4: xray.transport.internet.headers.http.Status
(*ResponseConfig)(nil), // 5: xray.transport.internet.headers.http.ResponseConfig
(*Config)(nil), // 6: xray.transport.internet.headers.http.Config
}
var file_transport_internet_headers_http_config_proto_depIdxs = []int32{
1, // 0: xray.transport.internet.headers.http.RequestConfig.version:type_name -> xray.transport.internet.headers.http.Version
2, // 1: xray.transport.internet.headers.http.RequestConfig.method:type_name -> xray.transport.internet.headers.http.Method
0, // 2: xray.transport.internet.headers.http.RequestConfig.header:type_name -> xray.transport.internet.headers.http.Header
1, // 3: xray.transport.internet.headers.http.ResponseConfig.version:type_name -> xray.transport.internet.headers.http.Version
4, // 4: xray.transport.internet.headers.http.ResponseConfig.status:type_name -> xray.transport.internet.headers.http.Status
0, // 5: xray.transport.internet.headers.http.ResponseConfig.header:type_name -> xray.transport.internet.headers.http.Header
3, // 6: xray.transport.internet.headers.http.Config.request:type_name -> xray.transport.internet.headers.http.RequestConfig
5, // 7: xray.transport.internet.headers.http.Config.response:type_name -> xray.transport.internet.headers.http.ResponseConfig
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_http_config_proto_init() }
func file_transport_internet_headers_http_config_proto_init() {
if File_transport_internet_headers_http_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Header); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Version); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Method); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RequestConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Status); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ResponseConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_http_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_headers_http_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_http_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_http_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_http_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_http_config_proto = out.File
file_transport_internet_headers_http_config_proto_rawDesc = nil
file_transport_internet_headers_http_config_proto_goTypes = nil
file_transport_internet_headers_http_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,65 @@
syntax = "proto3";
package xray.transport.internet.headers.http;
option csharp_namespace = "Xray.Transport.Internet.Headers.Http";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/http";
option java_package = "com.xray.transport.internet.headers.http";
option java_multiple_files = true;
message Header {
// "Accept", "Cookie", etc
string name = 1;
// Each entry must be valid in one piece. Random entry will be chosen if
// multiple entries present.
repeated string value = 2;
}
// HTTP version. Default value "1.1".
message Version {
string value = 1;
}
// HTTP method. Default value "GET".
message Method {
string value = 1;
}
message RequestConfig {
// Full HTTP version like "1.1".
Version version = 1;
// GET, POST, CONNECT etc
Method method = 2;
// URI like "/login.php"
repeated string uri = 3;
repeated Header header = 4;
}
message Status {
// Status code. Default "200".
string code = 1;
// Statue reason. Default "OK".
string reason = 2;
}
message ResponseConfig {
Version version = 1;
Status status = 2;
repeated Header header = 3;
}
message Config {
// Settings for authenticating requests. If not set, client side will not send
// authenication header, and server side will bypass authentication.
RequestConfig request = 1;
// Settings for authenticating responses. If not set, client side will bypass
// authentication, and server side will not send authentication header.
ResponseConfig response = 2;
}

View file

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

View file

@ -0,0 +1,321 @@
package http
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
import (
"bufio"
"bytes"
"context"
"io"
"net"
"net/http"
"strings"
"time"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
)
const (
// CRLF is the line ending in HTTP header
CRLF = "\r\n"
// ENDING is the double line ending between HTTP header and body.
ENDING = CRLF + CRLF
// max length of HTTP header. Safety precaution for DDoS attack.
maxHeaderLength = 8192
)
var (
ErrHeaderToLong = newError("Header too long.")
ErrHeaderMisMatch = newError("Header Mismatch.")
)
type Reader interface {
Read(io.Reader) (*buf.Buffer, error)
}
type Writer interface {
Write(io.Writer) error
}
type NoOpReader struct{}
func (NoOpReader) Read(io.Reader) (*buf.Buffer, error) {
return nil, nil
}
type NoOpWriter struct{}
func (NoOpWriter) Write(io.Writer) error {
return nil
}
type HeaderReader struct {
req *http.Request
expectedHeader *RequestConfig
}
func (h *HeaderReader) ExpectThisRequest(expectedHeader *RequestConfig) *HeaderReader {
h.expectedHeader = expectedHeader
return h
}
func (h *HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
buffer := buf.New()
totalBytes := int32(0)
endingDetected := false
var headerBuf bytes.Buffer
for totalBytes < maxHeaderLength {
_, err := buffer.ReadFrom(reader)
if err != nil {
buffer.Release()
return nil, err
}
if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 {
headerBuf.Write(buffer.BytesRange(0, int32(n+len(ENDING))))
buffer.Advance(int32(n + len(ENDING)))
endingDetected = true
break
}
lenEnding := int32(len(ENDING))
if buffer.Len() >= lenEnding {
totalBytes += buffer.Len() - lenEnding
headerBuf.Write(buffer.BytesRange(0, buffer.Len()-lenEnding))
leftover := buffer.BytesFrom(-lenEnding)
buffer.Clear()
copy(buffer.Extend(lenEnding), leftover)
if _, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != io.ErrUnexpectedEOF {
return nil, err
}
}
}
if !endingDetected {
buffer.Release()
return nil, ErrHeaderToLong
}
if h.expectedHeader == nil {
if buffer.IsEmpty() {
buffer.Release()
return nil, nil
}
return buffer, nil
}
// Parse the request
if req, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != nil {
return nil, err
} else {
h.req = req
}
// Check req
path := h.req.URL.Path
hasThisURI := false
for _, u := range h.expectedHeader.Uri {
if u == path {
hasThisURI = true
}
}
if !hasThisURI {
return nil, ErrHeaderMisMatch
}
if buffer.IsEmpty() {
buffer.Release()
return nil, nil
}
return buffer, nil
}
type HeaderWriter struct {
header *buf.Buffer
}
func NewHeaderWriter(header *buf.Buffer) *HeaderWriter {
return &HeaderWriter{
header: header,
}
}
func (w *HeaderWriter) Write(writer io.Writer) error {
if w.header == nil {
return nil
}
err := buf.WriteAllBytes(writer, w.header.Bytes())
w.header.Release()
w.header = nil
return err
}
type Conn struct {
net.Conn
readBuffer *buf.Buffer
oneTimeReader Reader
oneTimeWriter Writer
errorWriter Writer
errorMismatchWriter Writer
errorTooLongWriter Writer
errReason error
}
func NewConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer, errorMismatchWriter Writer, errorTooLongWriter Writer) *Conn {
return &Conn{
Conn: conn,
oneTimeReader: reader,
oneTimeWriter: writer,
errorWriter: errorWriter,
errorMismatchWriter: errorMismatchWriter,
errorTooLongWriter: errorTooLongWriter,
}
}
func (c *Conn) Read(b []byte) (int, error) {
if c.oneTimeReader != nil {
buffer, err := c.oneTimeReader.Read(c.Conn)
if err != nil {
c.errReason = err
return 0, err
}
c.readBuffer = buffer
c.oneTimeReader = nil
}
if !c.readBuffer.IsEmpty() {
nBytes, _ := c.readBuffer.Read(b)
if c.readBuffer.IsEmpty() {
c.readBuffer.Release()
c.readBuffer = nil
}
return nBytes, nil
}
return c.Conn.Read(b)
}
// Write implements io.Writer.
func (c *Conn) Write(b []byte) (int, error) {
if c.oneTimeWriter != nil {
err := c.oneTimeWriter.Write(c.Conn)
c.oneTimeWriter = nil
if err != nil {
return 0, err
}
}
return c.Conn.Write(b)
}
// Close implements net.Conn.Close().
func (c *Conn) Close() error {
if c.oneTimeWriter != nil && c.errorWriter != nil {
// Connection is being closed but header wasn't sent. This means the client request
// is probably not valid. Sending back a server error header in this case.
// Write response based on error reason
switch c.errReason {
case ErrHeaderMisMatch:
c.errorMismatchWriter.Write(c.Conn)
case ErrHeaderToLong:
c.errorTooLongWriter.Write(c.Conn)
default:
c.errorWriter.Write(c.Conn)
}
}
return c.Conn.Close()
}
func formResponseHeader(config *ResponseConfig) *HeaderWriter {
header := buf.New()
common.Must2(header.WriteString(strings.Join([]string{config.GetFullVersion(), config.GetStatusValue().Code, config.GetStatusValue().Reason}, " ")))
common.Must2(header.WriteString(CRLF))
headers := config.PickHeaders()
for _, h := range headers {
common.Must2(header.WriteString(h))
common.Must2(header.WriteString(CRLF))
}
if !config.HasHeader("Date") {
common.Must2(header.WriteString("Date: "))
common.Must2(header.WriteString(time.Now().Format(http.TimeFormat)))
common.Must2(header.WriteString(CRLF))
}
common.Must2(header.WriteString(CRLF))
return &HeaderWriter{
header: header,
}
}
type Authenticator struct {
config *Config
}
func (a Authenticator) GetClientWriter() *HeaderWriter {
header := buf.New()
config := a.config.Request
common.Must2(header.WriteString(strings.Join([]string{config.GetMethodValue(), config.PickURI(), config.GetFullVersion()}, " ")))
common.Must2(header.WriteString(CRLF))
headers := config.PickHeaders()
for _, h := range headers {
common.Must2(header.WriteString(h))
common.Must2(header.WriteString(CRLF))
}
common.Must2(header.WriteString(CRLF))
return &HeaderWriter{
header: header,
}
}
func (a Authenticator) GetServerWriter() *HeaderWriter {
return formResponseHeader(a.config.Response)
}
func (a Authenticator) Client(conn net.Conn) net.Conn {
if a.config.Request == nil && a.config.Response == nil {
return conn
}
var reader Reader = NoOpReader{}
if a.config.Request != nil {
reader = new(HeaderReader)
}
var writer Writer = NoOpWriter{}
if a.config.Response != nil {
writer = a.GetClientWriter()
}
return NewConn(conn, reader, writer, NoOpWriter{}, NoOpWriter{}, NoOpWriter{})
}
func (a Authenticator) Server(conn net.Conn) net.Conn {
if a.config.Request == nil && a.config.Response == nil {
return conn
}
return NewConn(conn, new(HeaderReader).ExpectThisRequest(a.config.Request), a.GetServerWriter(),
formResponseHeader(resp400),
formResponseHeader(resp404),
formResponseHeader(resp400))
}
func NewAuthenticator(ctx context.Context, config *Config) (Authenticator, error) {
return Authenticator{
config: config,
}, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewAuthenticator(ctx, config.(*Config))
}))
}

View file

@ -0,0 +1,305 @@
package http_test
import (
"bufio"
"bytes"
"context"
"crypto/rand"
"strings"
"testing"
"time"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
. "github.com/xtls/xray-core/v1/transport/internet/headers/http"
)
func TestReaderWriter(t *testing.T) {
cache := buf.New()
b := buf.New()
common.Must2(b.WriteString("abcd" + ENDING))
writer := NewHeaderWriter(b)
err := writer.Write(cache)
common.Must(err)
if v := cache.Len(); v != 8 {
t.Error("cache len: ", v)
}
_, err = cache.Write([]byte{'e', 'f', 'g'})
common.Must(err)
reader := &HeaderReader{}
_, err = reader.Read(cache)
if err != nil && !strings.HasPrefix(err.Error(), "malformed HTTP request") {
t.Error("unknown error ", err)
}
}
func TestRequestHeader(t *testing.T) {
auth, err := NewAuthenticator(context.Background(), &Config{
Request: &RequestConfig{
Uri: []string{"/"},
Header: []*Header{
{
Name: "Test",
Value: []string{"Value"},
},
},
},
})
common.Must(err)
cache := buf.New()
err = auth.GetClientWriter().Write(cache)
common.Must(err)
if cache.String() != "GET / HTTP/1.1\r\nTest: Value\r\n\r\n" {
t.Error("cache: ", cache.String())
}
}
func TestLongRequestHeader(t *testing.T) {
payload := make([]byte, buf.Size+2)
common.Must2(rand.Read(payload[:buf.Size-2]))
copy(payload[buf.Size-2:], ENDING)
payload = append(payload, []byte("abcd")...)
reader := HeaderReader{}
_, err := reader.Read(bytes.NewReader(payload))
if err != nil && !(strings.HasPrefix(err.Error(), "invalid") || strings.HasPrefix(err.Error(), "malformed")) {
t.Error("unknown error ", err)
}
}
func TestConnection(t *testing.T) {
auth, err := NewAuthenticator(context.Background(), &Config{
Request: &RequestConfig{
Method: &Method{Value: "Post"},
Uri: []string{"/testpath"},
Header: []*Header{
{
Name: "Host",
Value: []string{"www.example.com", "www.google.com"},
},
{
Name: "User-Agent",
Value: []string{"Test-Agent"},
},
},
},
Response: &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "404",
Reason: "Not Found",
},
},
})
common.Must(err)
listener, err := net.Listen("tcp", "127.0.0.1:0")
common.Must(err)
go func() {
conn, err := listener.Accept()
common.Must(err)
authConn := auth.Server(conn)
b := make([]byte, 256)
for {
n, err := authConn.Read(b)
if err != nil {
break
}
_, err = authConn.Write(b[:n])
common.Must(err)
}
}()
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
common.Must(err)
authConn := auth.Client(conn)
defer authConn.Close()
authConn.Write([]byte("Test payload"))
authConn.Write([]byte("Test payload 2"))
expectedResponse := "Test payloadTest payload 2"
actualResponse := make([]byte, 256)
deadline := time.Now().Add(time.Second * 5)
totalBytes := 0
for {
n, err := authConn.Read(actualResponse[totalBytes:])
common.Must(err)
totalBytes += n
if totalBytes >= len(expectedResponse) || time.Now().After(deadline) {
break
}
}
if string(actualResponse[:totalBytes]) != expectedResponse {
t.Error("response: ", string(actualResponse[:totalBytes]))
}
}
func TestConnectionInvPath(t *testing.T) {
auth, err := NewAuthenticator(context.Background(), &Config{
Request: &RequestConfig{
Method: &Method{Value: "Post"},
Uri: []string{"/testpath"},
Header: []*Header{
{
Name: "Host",
Value: []string{"www.example.com", "www.google.com"},
},
{
Name: "User-Agent",
Value: []string{"Test-Agent"},
},
},
},
Response: &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "404",
Reason: "Not Found",
},
},
})
common.Must(err)
authR, err := NewAuthenticator(context.Background(), &Config{
Request: &RequestConfig{
Method: &Method{Value: "Post"},
Uri: []string{"/testpathErr"},
Header: []*Header{
{
Name: "Host",
Value: []string{"www.example.com", "www.google.com"},
},
{
Name: "User-Agent",
Value: []string{"Test-Agent"},
},
},
},
Response: &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "404",
Reason: "Not Found",
},
},
})
common.Must(err)
listener, err := net.Listen("tcp", "127.0.0.1:0")
common.Must(err)
go func() {
conn, err := listener.Accept()
common.Must(err)
authConn := auth.Server(conn)
b := make([]byte, 256)
for {
n, err := authConn.Read(b)
if err != nil {
authConn.Close()
break
}
_, err = authConn.Write(b[:n])
common.Must(err)
}
}()
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
common.Must(err)
authConn := authR.Client(conn)
defer authConn.Close()
authConn.Write([]byte("Test payload"))
authConn.Write([]byte("Test payload 2"))
expectedResponse := "Test payloadTest payload 2"
actualResponse := make([]byte, 256)
deadline := time.Now().Add(time.Second * 5)
totalBytes := 0
for {
n, err := authConn.Read(actualResponse[totalBytes:])
if err == nil {
t.Error("Error Expected", err)
} else {
return
}
totalBytes += n
if totalBytes >= len(expectedResponse) || time.Now().After(deadline) {
break
}
}
}
func TestConnectionInvReq(t *testing.T) {
auth, err := NewAuthenticator(context.Background(), &Config{
Request: &RequestConfig{
Method: &Method{Value: "Post"},
Uri: []string{"/testpath"},
Header: []*Header{
{
Name: "Host",
Value: []string{"www.example.com", "www.google.com"},
},
{
Name: "User-Agent",
Value: []string{"Test-Agent"},
},
},
},
Response: &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "404",
Reason: "Not Found",
},
},
})
common.Must(err)
listener, err := net.Listen("tcp", "127.0.0.1:0")
common.Must(err)
go func() {
conn, err := listener.Accept()
common.Must(err)
authConn := auth.Server(conn)
b := make([]byte, 256)
for {
n, err := authConn.Read(b)
if err != nil {
authConn.Close()
break
}
_, err = authConn.Write(b[:n])
common.Must(err)
}
}()
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
common.Must(err)
conn.Write([]byte("ABCDEFGHIJKMLN\r\n\r\n"))
l, _, err := bufio.NewReader(conn).ReadLine()
common.Must(err)
if !strings.HasPrefix(string(l), "HTTP/1.1 400 Bad Request") {
t.Error("Resp to non http conn", string(l))
}
}

View file

@ -0,0 +1,11 @@
package http
import (
"bufio"
"net/http"
_ "unsafe" // required to use //go:linkname
)
//go:linkname readRequest net/http.readRequest
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *http.Request, err error)

View file

@ -0,0 +1,49 @@
package http
var resp400 = &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "400",
Reason: "Bad Request",
},
Header: []*Header{
{
Name: "Connection",
Value: []string{"close"},
},
{
Name: "Cache-Control",
Value: []string{"private"},
},
{
Name: "Content-Length",
Value: []string{"0"},
},
},
}
var resp404 = &ResponseConfig{
Version: &Version{
Value: "1.1",
},
Status: &Status{
Code: "404",
Reason: "Not Found",
},
Header: []*Header{
{
Name: "Connection",
Value: []string{"close"},
},
{
Name: "Cache-Control",
Value: []string{"private"},
},
{
Name: "Content-Length",
Value: []string{"0"},
},
},
}

View file

@ -0,0 +1,200 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/noop/config.proto
package noop
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_noop_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_headers_noop_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_headers_noop_config_proto_rawDescGZIP(), []int{0}
}
type ConnectionConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ConnectionConfig) Reset() {
*x = ConnectionConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ConnectionConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ConnectionConfig) ProtoMessage() {}
func (x *ConnectionConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_noop_config_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 ConnectionConfig.ProtoReflect.Descriptor instead.
func (*ConnectionConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_noop_config_proto_rawDescGZIP(), []int{1}
}
var File_transport_internet_headers_noop_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_noop_config_proto_rawDesc = []byte{
0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x6e, 0x6f, 0x6f,
0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
0x6e, 0x6f, 0x6f, 0x70, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x12,
0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x42, 0x91, 0x01, 0x0a, 0x28, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x6f, 0x6f, 0x70, 0x50,
0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x6e, 0x6f, 0x6f, 0x70, 0xaa,
0x02, 0x24, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
0x73, 0x2e, 0x4e, 0x6f, 0x6f, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_noop_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_noop_config_proto_rawDescData = file_transport_internet_headers_noop_config_proto_rawDesc
)
func file_transport_internet_headers_noop_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_noop_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_noop_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_noop_config_proto_rawDescData)
})
return file_transport_internet_headers_noop_config_proto_rawDescData
}
var file_transport_internet_headers_noop_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_transport_internet_headers_noop_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.headers.noop.Config
(*ConnectionConfig)(nil), // 1: xray.transport.internet.headers.noop.ConnectionConfig
}
var file_transport_internet_headers_noop_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_noop_config_proto_init() }
func file_transport_internet_headers_noop_config_proto_init() {
if File_transport_internet_headers_noop_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_noop_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_headers_noop_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ConnectionConfig); 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_headers_noop_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_noop_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_noop_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_noop_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_noop_config_proto = out.File
file_transport_internet_headers_noop_config_proto_rawDesc = nil
file_transport_internet_headers_noop_config_proto_goTypes = nil
file_transport_internet_headers_noop_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,11 @@
syntax = "proto3";
package xray.transport.internet.headers.noop;
option csharp_namespace = "Xray.Transport.Internet.Headers.Noop";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/noop";
option java_package = "com.xray.transport.internet.headers.noop";
option java_multiple_files = true;
message Config {}
message ConnectionConfig {}

View file

@ -0,0 +1,40 @@
package noop
import (
"context"
"net"
"github.com/xtls/xray-core/v1/common"
)
type NoOpHeader struct{}
func (NoOpHeader) Size() int32 {
return 0
}
// Serialize implements PacketHeader.
func (NoOpHeader) Serialize([]byte) {}
func NewNoOpHeader(context.Context, interface{}) (interface{}, error) {
return NoOpHeader{}, nil
}
type NoOpConnectionHeader struct{}
func (NoOpConnectionHeader) Client(conn net.Conn) net.Conn {
return conn
}
func (NoOpConnectionHeader) Server(conn net.Conn) net.Conn {
return conn
}
func NewNoOpConnectionHeader(context.Context, interface{}) (interface{}, error) {
return NoOpConnectionHeader{}, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), NewNoOpHeader))
common.Must(common.RegisterConfig((*ConnectionConfig)(nil), NewNoOpConnectionHeader))
}

View file

@ -0,0 +1,208 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/srtp/config.proto
package srtp
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Padding bool `protobuf:"varint,2,opt,name=padding,proto3" json:"padding,omitempty"`
Extension bool `protobuf:"varint,3,opt,name=extension,proto3" json:"extension,omitempty"`
CsrcCount uint32 `protobuf:"varint,4,opt,name=csrc_count,json=csrcCount,proto3" json:"csrc_count,omitempty"`
Marker bool `protobuf:"varint,5,opt,name=marker,proto3" json:"marker,omitempty"`
PayloadType uint32 `protobuf:"varint,6,opt,name=payload_type,json=payloadType,proto3" json:"payload_type,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_srtp_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_headers_srtp_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_headers_srtp_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *Config) GetPadding() bool {
if x != nil {
return x.Padding
}
return false
}
func (x *Config) GetExtension() bool {
if x != nil {
return x.Extension
}
return false
}
func (x *Config) GetCsrcCount() uint32 {
if x != nil {
return x.CsrcCount
}
return 0
}
func (x *Config) GetMarker() bool {
if x != nil {
return x.Marker
}
return false
}
func (x *Config) GetPayloadType() uint32 {
if x != nil {
return x.PayloadType
}
return 0
}
var File_transport_internet_headers_srtp_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_srtp_config_proto_rawDesc = []byte{
0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x72, 0x74,
0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
0x73, 0x72, 0x74, 0x70, 0x22, 0xb4, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64,
0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64,
0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
0x52, 0x06, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6c,
0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b,
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x42, 0x91, 0x01, 0x0a, 0x28,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64,
0x65, 0x72, 0x73, 0x2e, 0x73, 0x72, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64,
0x65, 0x72, 0x73, 0x2f, 0x73, 0x72, 0x74, 0x70, 0xaa, 0x02, 0x24, 0x58, 0x72, 0x61, 0x79, 0x2e,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x53, 0x72, 0x74, 0x70, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_srtp_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_srtp_config_proto_rawDescData = file_transport_internet_headers_srtp_config_proto_rawDesc
)
func file_transport_internet_headers_srtp_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_srtp_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_srtp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_srtp_config_proto_rawDescData)
})
return file_transport_internet_headers_srtp_config_proto_rawDescData
}
var file_transport_internet_headers_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_headers_srtp_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.headers.srtp.Config
}
var file_transport_internet_headers_srtp_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_srtp_config_proto_init() }
func file_transport_internet_headers_srtp_config_proto_init() {
if File_transport_internet_headers_srtp_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_srtp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_headers_srtp_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_srtp_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_srtp_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_srtp_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_srtp_config_proto = out.File
file_transport_internet_headers_srtp_config_proto_rawDesc = nil
file_transport_internet_headers_srtp_config_proto_goTypes = nil
file_transport_internet_headers_srtp_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,16 @@
syntax = "proto3";
package xray.transport.internet.headers.srtp;
option csharp_namespace = "Xray.Transport.Internet.Headers.Srtp";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/srtp";
option java_package = "com.xray.transport.internet.headers.srtp";
option java_multiple_files = true;
message Config {
uint32 version = 1;
bool padding = 2;
bool extension = 3;
uint32 csrc_count = 4;
bool marker = 5;
uint32 payload_type = 6;
}

View file

@ -0,0 +1,37 @@
package srtp
import (
"context"
"encoding/binary"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/dice"
)
type SRTP struct {
header uint16
number uint16
}
func (*SRTP) Size() int32 {
return 4
}
// Serialize implements PacketHeader.
func (s *SRTP) Serialize(b []byte) {
s.number++
binary.BigEndian.PutUint16(b, s.header)
binary.BigEndian.PutUint16(b[2:], s.number)
}
// New returns a new SRTP instance based on the given config.
func New(ctx context.Context, config interface{}) (interface{}, error) {
return &SRTP{
header: 0xB5E8,
number: dice.RollUint16(),
}, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), New))
}

View file

@ -0,0 +1,27 @@
package srtp_test
import (
"context"
"testing"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
. "github.com/xtls/xray-core/v1/transport/internet/headers/srtp"
)
func TestSRTPWrite(t *testing.T) {
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
srtpRaw, err := New(context.Background(), &Config{})
common.Must(err)
srtp := srtpRaw.(*SRTP)
payload := buf.New()
srtp.Serialize(payload.Extend(srtp.Size()))
payload.Write(content)
expectedLen := int32(len(content)) + srtp.Size()
if payload.Len() != expectedLen {
t.Error("expected ", expectedLen, " of bytes, but got ", payload.Len())
}
}

View file

@ -0,0 +1,148 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/tls/config.proto
package tls
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type PacketConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *PacketConfig) Reset() {
*x = PacketConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PacketConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PacketConfig) ProtoMessage() {}
func (x *PacketConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_tls_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 PacketConfig.ProtoReflect.Descriptor instead.
func (*PacketConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_tls_config_proto_rawDescGZIP(), []int{0}
}
var File_transport_internet_headers_tls_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_tls_config_proto_rawDesc = []byte{
0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x74, 0x6c, 0x73,
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x74,
0x6c, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x42, 0x8e, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01,
0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x23,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_tls_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_tls_config_proto_rawDescData = file_transport_internet_headers_tls_config_proto_rawDesc
)
func file_transport_internet_headers_tls_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_tls_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_tls_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_tls_config_proto_rawDescData)
})
return file_transport_internet_headers_tls_config_proto_rawDescData
}
var file_transport_internet_headers_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_headers_tls_config_proto_goTypes = []interface{}{
(*PacketConfig)(nil), // 0: xray.transport.internet.headers.tls.PacketConfig
}
var file_transport_internet_headers_tls_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_tls_config_proto_init() }
func file_transport_internet_headers_tls_config_proto_init() {
if File_transport_internet_headers_tls_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_tls_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PacketConfig); 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_headers_tls_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_tls_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_tls_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_tls_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_tls_config_proto = out.File
file_transport_internet_headers_tls_config_proto_rawDesc = nil
file_transport_internet_headers_tls_config_proto_goTypes = nil
file_transport_internet_headers_tls_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,9 @@
syntax = "proto3";
package xray.transport.internet.headers.tls;
option csharp_namespace = "Xray.Transport.Internet.Headers.Tls";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/tls";
option java_package = "com.xray.transport.internet.headers.tls";
option java_multiple_files = true;
message PacketConfig {}

View file

@ -0,0 +1,55 @@
package tls
import (
"context"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/dice"
)
// DTLS writes header as DTLS. See https://tools.ietf.org/html/rfc6347
type DTLS struct {
epoch uint16
length uint16
sequence uint32
}
// Size implements PacketHeader.
func (*DTLS) Size() int32 {
return 1 + 2 + 2 + 6 + 2
}
// Serialize implements PacketHeader.
func (d *DTLS) Serialize(b []byte) {
b[0] = 23 // application data
b[1] = 254
b[2] = 253
b[3] = byte(d.epoch >> 8)
b[4] = byte(d.epoch)
b[5] = 0
b[6] = 0
b[7] = byte(d.sequence >> 24)
b[8] = byte(d.sequence >> 16)
b[9] = byte(d.sequence >> 8)
b[10] = byte(d.sequence)
d.sequence++
b[11] = byte(d.length >> 8)
b[12] = byte(d.length)
d.length += 17
if d.length > 100 {
d.length -= 50
}
}
// New creates a new UTP header for the given config.
func New(ctx context.Context, config interface{}) (interface{}, error) {
return &DTLS{
epoch: dice.RollUint16(),
sequence: 0,
length: 17,
}, nil
}
func init() {
common.Must(common.RegisterConfig((*PacketConfig)(nil), New))
}

View file

@ -0,0 +1,26 @@
package tls_test
import (
"context"
"testing"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
. "github.com/xtls/xray-core/v1/transport/internet/headers/tls"
)
func TestDTLSWrite(t *testing.T) {
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
dtlsRaw, err := New(context.Background(), &PacketConfig{})
common.Must(err)
dtls := dtlsRaw.(*DTLS)
payload := buf.New()
dtls.Serialize(payload.Extend(dtls.Size()))
payload.Write(content)
if payload.Len() != int32(len(content))+dtls.Size() {
t.Error("payload len: ", payload.Len(), " want ", int32(len(content))+dtls.Size())
}
}

View file

@ -0,0 +1,158 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/utp/config.proto
package utp
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_utp_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_headers_utp_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_headers_utp_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
var File_transport_internet_headers_utp_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_utp_config_proto_rawDesc = []byte{
0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x74, 0x70,
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x75,
0x74, 0x70, 0x22, 0x22, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x8e, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x75,
0x74, 0x70, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x74,
0x70, 0xaa, 0x02, 0x23, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64,
0x65, 0x72, 0x73, 0x2e, 0x55, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_utp_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_utp_config_proto_rawDescData = file_transport_internet_headers_utp_config_proto_rawDesc
)
func file_transport_internet_headers_utp_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_utp_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_utp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_utp_config_proto_rawDescData)
})
return file_transport_internet_headers_utp_config_proto_rawDescData
}
var file_transport_internet_headers_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_headers_utp_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.headers.utp.Config
}
var file_transport_internet_headers_utp_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_utp_config_proto_init() }
func file_transport_internet_headers_utp_config_proto_init() {
if File_transport_internet_headers_utp_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_utp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_headers_utp_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_utp_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_utp_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_utp_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_utp_config_proto = out.File
file_transport_internet_headers_utp_config_proto_rawDesc = nil
file_transport_internet_headers_utp_config_proto_goTypes = nil
file_transport_internet_headers_utp_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,11 @@
syntax = "proto3";
package xray.transport.internet.headers.utp;
option csharp_namespace = "Xray.Transport.Internet.Headers.Utp";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/utp";
option java_package = "com.xray.transport.internet.headers.utp";
option java_multiple_files = true;
message Config {
uint32 version = 1;
}

View file

@ -0,0 +1,39 @@
package utp
import (
"context"
"encoding/binary"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/dice"
)
type UTP struct {
header byte
extension byte
connectionID uint16
}
func (*UTP) Size() int32 {
return 4
}
// Serialize implements PacketHeader.
func (u *UTP) Serialize(b []byte) {
binary.BigEndian.PutUint16(b, u.connectionID)
b[2] = u.header
b[3] = u.extension
}
// New creates a new UTP header for the given config.
func New(ctx context.Context, config interface{}) (interface{}, error) {
return &UTP{
header: 1,
extension: 0,
connectionID: dice.RollUint16(),
}, nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), New))
}

View file

@ -0,0 +1,26 @@
package utp_test
import (
"context"
"testing"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
. "github.com/xtls/xray-core/v1/transport/internet/headers/utp"
)
func TestUTPWrite(t *testing.T) {
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
utpRaw, err := New(context.Background(), &Config{})
common.Must(err)
utp := utpRaw.(*UTP)
payload := buf.New()
utp.Serialize(payload.Extend(utp.Size()))
payload.Write(content)
if payload.Len() != int32(len(content))+utp.Size() {
t.Error("unexpected payload length: ", payload.Len())
}
}

View file

@ -0,0 +1,149 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/wechat/config.proto
package wechat
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type VideoConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *VideoConfig) Reset() {
*x = VideoConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VideoConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VideoConfig) ProtoMessage() {}
func (x *VideoConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_wechat_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 VideoConfig.ProtoReflect.Descriptor instead.
func (*VideoConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_wechat_config_proto_rawDescGZIP(), []int{0}
}
var File_transport_internet_headers_wechat_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_wechat_config_proto_rawDesc = []byte{
0x0a, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77, 0x65, 0x63,
0x68, 0x61, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x26, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x73, 0x2e, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x22, 0x0d, 0x0a, 0x0b, 0x56, 0x69, 0x64, 0x65,
0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x97, 0x01, 0x0a, 0x2a, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x73, 0x2f, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0xaa, 0x02, 0x26, 0x58, 0x72, 0x61, 0x79, 0x2e,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x57, 0x65, 0x63, 0x68, 0x61,
0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_wechat_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_wechat_config_proto_rawDescData = file_transport_internet_headers_wechat_config_proto_rawDesc
)
func file_transport_internet_headers_wechat_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_wechat_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_wechat_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_wechat_config_proto_rawDescData)
})
return file_transport_internet_headers_wechat_config_proto_rawDescData
}
var file_transport_internet_headers_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_headers_wechat_config_proto_goTypes = []interface{}{
(*VideoConfig)(nil), // 0: xray.transport.internet.headers.wechat.VideoConfig
}
var file_transport_internet_headers_wechat_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_wechat_config_proto_init() }
func file_transport_internet_headers_wechat_config_proto_init() {
if File_transport_internet_headers_wechat_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_wechat_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VideoConfig); 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_headers_wechat_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_wechat_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_wechat_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_wechat_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_wechat_config_proto = out.File
file_transport_internet_headers_wechat_config_proto_rawDesc = nil
file_transport_internet_headers_wechat_config_proto_goTypes = nil
file_transport_internet_headers_wechat_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,9 @@
syntax = "proto3";
package xray.transport.internet.headers.wechat;
option csharp_namespace = "Xray.Transport.Internet.Headers.Wechat";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/wechat";
option java_package = "com.xray.transport.internet.headers.wechat";
option java_multiple_files = true;
message VideoConfig {}

View file

@ -0,0 +1,43 @@
package wechat
import (
"context"
"encoding/binary"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/dice"
)
type VideoChat struct {
sn uint32
}
func (vc *VideoChat) Size() int32 {
return 13
}
// Serialize implements PacketHeader.
func (vc *VideoChat) Serialize(b []byte) {
vc.sn++
b[0] = 0xa1
b[1] = 0x08
binary.BigEndian.PutUint32(b[2:], vc.sn) // b[2:6]
b[6] = 0x00
b[7] = 0x10
b[8] = 0x11
b[9] = 0x18
b[10] = 0x30
b[11] = 0x22
b[12] = 0x30
}
// NewVideoChat returns a new VideoChat instance based on given config.
func NewVideoChat(ctx context.Context, config interface{}) (interface{}, error) {
return &VideoChat{
sn: uint32(dice.RollUint16()),
}, nil
}
func init() {
common.Must(common.RegisterConfig((*VideoConfig)(nil), NewVideoChat))
}

View file

@ -0,0 +1,24 @@
package wechat_test
import (
"context"
"testing"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
. "github.com/xtls/xray-core/v1/transport/internet/headers/wechat"
)
func TestUTPWrite(t *testing.T) {
videoRaw, err := NewVideoChat(context.Background(), &VideoConfig{})
common.Must(err)
video := videoRaw.(*VideoChat)
payload := buf.New()
video.Serialize(payload.Extend(video.Size()))
if payload.Len() != video.Size() {
t.Error("expected payload size ", video.Size(), " but got ", payload.Len())
}
}

View file

@ -0,0 +1,150 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/headers/wireguard/config.proto
package wireguard
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type WireguardConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *WireguardConfig) Reset() {
*x = WireguardConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *WireguardConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WireguardConfig) ProtoMessage() {}
func (x *WireguardConfig) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_headers_wireguard_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 WireguardConfig.ProtoReflect.Descriptor instead.
func (*WireguardConfig) Descriptor() ([]byte, []int) {
return file_transport_internet_headers_wireguard_config_proto_rawDescGZIP(), []int{0}
}
var File_transport_internet_headers_wireguard_config_proto protoreflect.FileDescriptor
var file_transport_internet_headers_wireguard_config_proto_rawDesc = []byte{
0x0a, 0x31, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77, 0x69, 0x72,
0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x29, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x73, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x22, 0x11,
0x0a, 0x0f, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x42, 0xa0, 0x01, 0x0a, 0x2d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75,
0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77,
0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x29, 0x58, 0x72, 0x61, 0x79, 0x2e,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x67,
0x75, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_headers_wireguard_config_proto_rawDescOnce sync.Once
file_transport_internet_headers_wireguard_config_proto_rawDescData = file_transport_internet_headers_wireguard_config_proto_rawDesc
)
func file_transport_internet_headers_wireguard_config_proto_rawDescGZIP() []byte {
file_transport_internet_headers_wireguard_config_proto_rawDescOnce.Do(func() {
file_transport_internet_headers_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_wireguard_config_proto_rawDescData)
})
return file_transport_internet_headers_wireguard_config_proto_rawDescData
}
var file_transport_internet_headers_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_headers_wireguard_config_proto_goTypes = []interface{}{
(*WireguardConfig)(nil), // 0: xray.transport.internet.headers.wireguard.WireguardConfig
}
var file_transport_internet_headers_wireguard_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_headers_wireguard_config_proto_init() }
func file_transport_internet_headers_wireguard_config_proto_init() {
if File_transport_internet_headers_wireguard_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_headers_wireguard_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WireguardConfig); 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_headers_wireguard_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_headers_wireguard_config_proto_goTypes,
DependencyIndexes: file_transport_internet_headers_wireguard_config_proto_depIdxs,
MessageInfos: file_transport_internet_headers_wireguard_config_proto_msgTypes,
}.Build()
File_transport_internet_headers_wireguard_config_proto = out.File
file_transport_internet_headers_wireguard_config_proto_rawDesc = nil
file_transport_internet_headers_wireguard_config_proto_goTypes = nil
file_transport_internet_headers_wireguard_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,9 @@
syntax = "proto3";
package xray.transport.internet.headers.wireguard;
option csharp_namespace = "Xray.Transport.Internet.Headers.Wireguard";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/wireguard";
option java_package = "com.xray.transport.internet.headers.wireguard";
option java_multiple_files = true;
message WireguardConfig {}

View file

@ -0,0 +1,30 @@
package wireguard
import (
"context"
"github.com/xtls/xray-core/v1/common"
)
type Wireguard struct{}
func (Wireguard) Size() int32 {
return 4
}
// Serialize implements PacketHeader.
func (Wireguard) Serialize(b []byte) {
b[0] = 0x04
b[1] = 0x00
b[2] = 0x00
b[3] = 0x00
}
// NewWireguard returns a new VideoChat instance based on given config.
func NewWireguard(ctx context.Context, config interface{}) (interface{}, error) {
return Wireguard{}, nil
}
func init() {
common.Must(common.RegisterConfig((*WireguardConfig)(nil), NewWireguard))
}

View file

@ -0,0 +1,49 @@
// +build !confonly
package http
import (
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/dice"
"github.com/xtls/xray-core/v1/transport/internet"
)
const protocolName = "http"
func (c *Config) getHosts() []string {
if len(c.Host) == 0 {
return []string{"www.example.com"}
}
return c.Host
}
func (c *Config) isValidHost(host string) bool {
hosts := c.getHosts()
for _, h := range hosts {
if h == host {
return true
}
}
return false
}
func (c *Config) getRandomHost() string {
hosts := c.getHosts()
return hosts[dice.Roll(len(hosts))]
}
func (c *Config) getNormalizedPath() string {
if c.Path == "" {
return "/"
}
if c.Path[0] != '/' {
return "/" + c.Path
}
return c.Path
}
func init() {
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
return new(Config)
}))
}

View file

@ -0,0 +1,165 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/http/config.proto
package http
import (
proto "github.com/golang/protobuf/proto"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_http_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_http_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_http_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetHost() []string {
if x != nil {
return x.Host
}
return nil
}
func (x *Config) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
var File_transport_internet_http_config_proto protoreflect.FileDescriptor
var file_transport_internet_http_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, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x68, 0x74, 0x74, 0x70, 0x22, 0x30, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f,
0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x79, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74,
0x74, 0x70, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x74, 0x74,
0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_transport_internet_http_config_proto_rawDescOnce sync.Once
file_transport_internet_http_config_proto_rawDescData = file_transport_internet_http_config_proto_rawDesc
)
func file_transport_internet_http_config_proto_rawDescGZIP() []byte {
file_transport_internet_http_config_proto_rawDescOnce.Do(func() {
file_transport_internet_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_http_config_proto_rawDescData)
})
return file_transport_internet_http_config_proto_rawDescData
}
var file_transport_internet_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_http_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.http.Config
}
var file_transport_internet_http_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_transport_internet_http_config_proto_init() }
func file_transport_internet_http_config_proto_init() {
if File_transport_internet_http_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_http_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_http_config_proto_goTypes,
DependencyIndexes: file_transport_internet_http_config_proto_depIdxs,
MessageInfos: file_transport_internet_http_config_proto_msgTypes,
}.Build()
File_transport_internet_http_config_proto = out.File
file_transport_internet_http_config_proto_rawDesc = nil
file_transport_internet_http_config_proto_goTypes = nil
file_transport_internet_http_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,12 @@
syntax = "proto3";
package xray.transport.internet.http;
option csharp_namespace = "Xray.Transport.Internet.Http";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/http";
option java_package = "com.xray.transport.internet.http";
option java_multiple_files = true;
message Config {
repeated string host = 1;
string path = 2;
}

View file

@ -0,0 +1,138 @@
// +build !confonly
package http
import (
"context"
gotls "crypto/tls"
"net/http"
"net/url"
"sync"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
"github.com/xtls/xray-core/v1/transport/pipe"
"golang.org/x/net/http2"
)
var (
globalDialerMap map[net.Destination]*http.Client
globalDialerAccess sync.Mutex
)
func getHTTPClient(_ context.Context, dest net.Destination, tlsSettings *tls.Config) (*http.Client, error) {
globalDialerAccess.Lock()
defer globalDialerAccess.Unlock()
if globalDialerMap == nil {
globalDialerMap = make(map[net.Destination]*http.Client)
}
if client, found := globalDialerMap[dest]; found {
return client, nil
}
transport := &http2.Transport{
DialTLS: func(network string, addr string, tlsConfig *gotls.Config) (net.Conn, error) {
rawHost, rawPort, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
if len(rawPort) == 0 {
rawPort = "443"
}
port, err := net.PortFromString(rawPort)
if err != nil {
return nil, err
}
address := net.ParseAddress(rawHost)
pconn, err := internet.DialSystem(context.Background(), net.TCPDestination(address, port), nil)
if err != nil {
return nil, err
}
cn := gotls.Client(pconn, tlsConfig)
if err := cn.Handshake(); err != nil {
return nil, err
}
if !tlsConfig.InsecureSkipVerify {
if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil {
return nil, err
}
}
state := cn.ConnectionState()
if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
return nil, newError("http2: unexpected ALPN protocol " + p + "; want q" + http2.NextProtoTLS).AtError()
}
if !state.NegotiatedProtocolIsMutual {
return nil, newError("http2: could not negotiate protocol mutually").AtError()
}
return cn, nil
},
TLSClientConfig: tlsSettings.GetTLSConfig(tls.WithDestination(dest)),
}
client := &http.Client{
Transport: transport,
}
globalDialerMap[dest] = client
return client, nil
}
// Dial dials a new TCP connection to the given destination.
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
httpSettings := streamSettings.ProtocolSettings.(*Config)
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
if tlsConfig == nil {
return nil, newError("TLS must be enabled for http transport.").AtWarning()
}
client, err := getHTTPClient(ctx, dest, tlsConfig)
if err != nil {
return nil, err
}
opts := pipe.OptionsFromContext(ctx)
preader, pwriter := pipe.New(opts...)
breader := &buf.BufferedReader{Reader: preader}
request := &http.Request{
Method: "PUT",
Host: httpSettings.getRandomHost(),
Body: breader,
URL: &url.URL{
Scheme: "https",
Host: dest.NetAddr(),
Path: httpSettings.getNormalizedPath(),
},
Proto: "HTTP/2",
ProtoMajor: 2,
ProtoMinor: 0,
Header: make(http.Header),
}
// Disable any compression method from server.
request.Header.Set("Accept-Encoding", "identity")
response, err := client.Do(request)
if err != nil {
return nil, newError("failed to dial to ", dest).Base(err).AtWarning()
}
if response.StatusCode != 200 {
return nil, newError("unexpected status", response.StatusCode).AtWarning()
}
bwriter := buf.NewBufferedWriter(pwriter)
common.Must(bwriter.SetBuffered(false))
return net.NewConnection(
net.ConnectionOutput(response.Body),
net.ConnectionInput(bwriter),
net.ConnectionOnClose(common.ChainedClosable{breader, bwriter, response.Body}),
), nil
}
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
}

View file

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

View file

@ -0,0 +1,3 @@
package http
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen

View file

@ -0,0 +1,94 @@
package http_test
import (
"context"
"crypto/rand"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/protocol/tls/cert"
"github.com/xtls/xray-core/v1/testing/servers/tcp"
"github.com/xtls/xray-core/v1/transport/internet"
. "github.com/xtls/xray-core/v1/transport/internet/http"
"github.com/xtls/xray-core/v1/transport/internet/tls"
)
func TestHTTPConnection(t *testing.T) {
port := tcp.PickPort()
listener, err := Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
ProtocolName: "http",
ProtocolSettings: &Config{},
SecurityType: "tls",
SecuritySettings: &tls.Config{
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("www.example.com")))},
},
}, func(conn internet.Connection) {
go func() {
defer conn.Close()
b := buf.New()
defer b.Release()
for {
if _, err := b.ReadFrom(conn); err != nil {
return
}
_, err := conn.Write(b.Bytes())
common.Must(err)
}
}()
})
common.Must(err)
defer listener.Close()
time.Sleep(time.Second)
dctx := context.Background()
conn, err := Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
ProtocolName: "http",
ProtocolSettings: &Config{},
SecurityType: "tls",
SecuritySettings: &tls.Config{
ServerName: "www.example.com",
AllowInsecure: true,
},
})
common.Must(err)
defer conn.Close()
const N = 1024
b1 := make([]byte, N)
common.Must2(rand.Read(b1))
b2 := buf.New()
nBytes, err := conn.Write(b1)
common.Must(err)
if nBytes != N {
t.Error("write: ", nBytes)
}
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
nBytes, err = conn.Write(b1)
common.Must(err)
if nBytes != N {
t.Error("write: ", nBytes)
}
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
}

View file

@ -0,0 +1,205 @@
// +build !confonly
package http
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
http_proto "github.com/xtls/xray-core/v1/common/protocol/http"
"github.com/xtls/xray-core/v1/common/serial"
"github.com/xtls/xray-core/v1/common/session"
"github.com/xtls/xray-core/v1/common/signal/done"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
)
type Listener struct {
server *http.Server
handler internet.ConnHandler
local net.Addr
config *Config
locker *internet.FileLocker // for unix domain socket
}
func (l *Listener) Addr() net.Addr {
return l.local
}
func (l *Listener) Close() error {
if l.locker != nil {
fmt.Fprintln(os.Stderr, "RELEASE LOCK")
l.locker.Release()
}
return l.server.Close()
}
type flushWriter struct {
w io.Writer
d *done.Instance
}
func (fw flushWriter) Write(p []byte) (n int, err error) {
if fw.d.Done() {
return 0, io.ErrClosedPipe
}
n, err = fw.w.Write(p)
if f, ok := fw.w.(http.Flusher); ok {
f.Flush()
}
return
}
func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
host := request.Host
if !l.config.isValidHost(host) {
writer.WriteHeader(404)
return
}
path := l.config.getNormalizedPath()
if !strings.HasPrefix(request.URL.Path, path) {
writer.WriteHeader(404)
return
}
writer.Header().Set("Cache-Control", "no-store")
writer.WriteHeader(200)
if f, ok := writer.(http.Flusher); ok {
f.Flush()
}
remoteAddr := l.Addr()
dest, err := net.ParseDestination(request.RemoteAddr)
if err != nil {
newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog()
} else {
remoteAddr = &net.TCPAddr{
IP: dest.Address.IP(),
Port: int(dest.Port),
}
}
forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
remoteAddr = &net.TCPAddr{
IP: forwardedAddrs[0].IP(),
Port: int(0),
}
}
done := done.New()
conn := net.NewConnection(
net.ConnectionOutput(request.Body),
net.ConnectionInput(flushWriter{w: writer, d: done}),
net.ConnectionOnClose(common.ChainedClosable{done, request.Body}),
net.ConnectionLocalAddr(l.Addr()),
net.ConnectionRemoteAddr(remoteAddr),
)
l.handler(conn)
<-done.Wait()
}
func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
httpSettings := streamSettings.ProtocolSettings.(*Config)
var listener *Listener
if port == net.Port(0) { // unix
listener = &Listener{
handler: handler,
local: &net.UnixAddr{
Name: address.Domain(),
Net: "unix",
},
config: httpSettings,
}
} else { // tcp
listener = &Listener{
handler: handler,
local: &net.TCPAddr{
IP: address.IP(),
Port: int(port),
},
config: httpSettings,
}
}
var server *http.Server
config := tls.ConfigFromStreamSettings(streamSettings)
if config == nil {
h2s := &http2.Server{}
server = &http.Server{
Addr: serial.Concat(address, ":", port),
Handler: h2c.NewHandler(listener, h2s),
ReadHeaderTimeout: time.Second * 4,
}
} else {
server = &http.Server{
Addr: serial.Concat(address, ":", port),
TLSConfig: config.GetTLSConfig(tls.WithNextProto("h2")),
Handler: listener,
ReadHeaderTimeout: time.Second * 4,
}
}
if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
}
listener.server = server
go func() {
var streamListener net.Listener
var err error
if port == net.Port(0) { // unix
streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{
Name: address.Domain(),
Net: "unix",
}, streamSettings.SocketSettings)
if err != nil {
newError("failed to listen on ", address).Base(err).WriteToLog(session.ExportIDToError(ctx))
return
}
locker := ctx.Value(address.Domain())
if locker != nil {
listener.locker = locker.(*internet.FileLocker)
}
} else { // tcp
streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{
IP: address.IP(),
Port: int(port),
}, streamSettings.SocketSettings)
if err != nil {
newError("failed to listen on ", address, ":", port).Base(err).WriteToLog(session.ExportIDToError(ctx))
return
}
}
if config == nil {
err = server.Serve(streamListener)
if err != nil {
newError("stoping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
} else {
err = server.ServeTLS(streamListener, "", "")
if err != nil {
newError("stoping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
}
}()
return listener, nil
}
func init() {
common.Must(internet.RegisterTransportListener(protocolName, Listen))
}

View file

@ -0,0 +1,3 @@
package internet
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen

View file

@ -0,0 +1,110 @@
// +build !confonly
package kcp
import (
"crypto/cipher"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/transport/internet"
)
const protocolName = "mkcp"
// GetMTUValue returns the value of MTU settings.
func (c *Config) GetMTUValue() uint32 {
if c == nil || c.Mtu == nil {
return 1350
}
return c.Mtu.Value
}
// GetTTIValue returns the value of TTI settings.
func (c *Config) GetTTIValue() uint32 {
if c == nil || c.Tti == nil {
return 50
}
return c.Tti.Value
}
// GetUplinkCapacityValue returns the value of UplinkCapacity settings.
func (c *Config) GetUplinkCapacityValue() uint32 {
if c == nil || c.UplinkCapacity == nil {
return 5
}
return c.UplinkCapacity.Value
}
// GetDownlinkCapacityValue returns the value of DownlinkCapacity settings.
func (c *Config) GetDownlinkCapacityValue() uint32 {
if c == nil || c.DownlinkCapacity == nil {
return 20
}
return c.DownlinkCapacity.Value
}
// GetWriteBufferSize returns the size of WriterBuffer in bytes.
func (c *Config) GetWriteBufferSize() uint32 {
if c == nil || c.WriteBuffer == nil {
return 2 * 1024 * 1024
}
return c.WriteBuffer.Size
}
// GetReadBufferSize returns the size of ReadBuffer in bytes.
func (c *Config) GetReadBufferSize() uint32 {
if c == nil || c.ReadBuffer == nil {
return 2 * 1024 * 1024
}
return c.ReadBuffer.Size
}
// GetSecurity returns the security settings.
func (c *Config) GetSecurity() (cipher.AEAD, error) {
if c.Seed != nil {
return NewAEADAESGCMBasedOnSeed(c.Seed.Seed), nil
}
return NewSimpleAuthenticator(), nil
}
func (c *Config) GetPackerHeader() (internet.PacketHeader, error) {
if c.HeaderConfig != nil {
rawConfig, err := c.HeaderConfig.GetInstance()
if err != nil {
return nil, err
}
return internet.CreatePacketHeader(rawConfig)
}
return nil, nil
}
func (c *Config) GetSendingInFlightSize() uint32 {
size := c.GetUplinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue())
if size < 8 {
size = 8
}
return size
}
func (c *Config) GetSendingBufferSize() uint32 {
return c.GetWriteBufferSize() / c.GetMTUValue()
}
func (c *Config) GetReceivingInFlightSize() uint32 {
size := c.GetDownlinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue())
if size < 8 {
size = 8
}
return size
}
func (c *Config) GetReceivingBufferSize() uint32 {
return c.GetReadBufferSize() / c.GetMTUValue()
}
func init() {
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
return new(Config)
}))
}

View file

@ -0,0 +1,774 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/kcp/config.proto
package kcp
import (
proto "github.com/golang/protobuf/proto"
serial "github.com/xtls/xray-core/v1/common/serial"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Maximum Transmission Unit, in bytes.
type MTU struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *MTU) Reset() {
*x = MTU{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MTU) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MTU) ProtoMessage() {}
func (x *MTU) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_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 MTU.ProtoReflect.Descriptor instead.
func (*MTU) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{0}
}
func (x *MTU) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
// Transmission Time Interview, in milli-sec.
type TTI struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *TTI) Reset() {
*x = TTI{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TTI) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TTI) ProtoMessage() {}
func (x *TTI) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_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 TTI.ProtoReflect.Descriptor instead.
func (*TTI) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{1}
}
func (x *TTI) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
// Uplink capacity, in MB.
type UplinkCapacity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *UplinkCapacity) Reset() {
*x = UplinkCapacity{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UplinkCapacity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UplinkCapacity) ProtoMessage() {}
func (x *UplinkCapacity) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[2]
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 UplinkCapacity.ProtoReflect.Descriptor instead.
func (*UplinkCapacity) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{2}
}
func (x *UplinkCapacity) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
// Downlink capacity, in MB.
type DownlinkCapacity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *DownlinkCapacity) Reset() {
*x = DownlinkCapacity{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DownlinkCapacity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DownlinkCapacity) ProtoMessage() {}
func (x *DownlinkCapacity) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[3]
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 DownlinkCapacity.ProtoReflect.Descriptor instead.
func (*DownlinkCapacity) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{3}
}
func (x *DownlinkCapacity) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
type WriteBuffer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Buffer size in bytes.
Size uint32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"`
}
func (x *WriteBuffer) Reset() {
*x = WriteBuffer{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *WriteBuffer) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WriteBuffer) ProtoMessage() {}
func (x *WriteBuffer) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[4]
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 WriteBuffer.ProtoReflect.Descriptor instead.
func (*WriteBuffer) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{4}
}
func (x *WriteBuffer) GetSize() uint32 {
if x != nil {
return x.Size
}
return 0
}
type ReadBuffer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Buffer size in bytes.
Size uint32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"`
}
func (x *ReadBuffer) Reset() {
*x = ReadBuffer{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReadBuffer) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReadBuffer) ProtoMessage() {}
func (x *ReadBuffer) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[5]
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 ReadBuffer.ProtoReflect.Descriptor instead.
func (*ReadBuffer) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{5}
}
func (x *ReadBuffer) GetSize() uint32 {
if x != nil {
return x.Size
}
return 0
}
type ConnectionReuse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"`
}
func (x *ConnectionReuse) Reset() {
*x = ConnectionReuse{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ConnectionReuse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ConnectionReuse) ProtoMessage() {}
func (x *ConnectionReuse) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[6]
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 ConnectionReuse.ProtoReflect.Descriptor instead.
func (*ConnectionReuse) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{6}
}
func (x *ConnectionReuse) GetEnable() bool {
if x != nil {
return x.Enable
}
return false
}
// Maximum Transmission Unit, in bytes.
type EncryptionSeed struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Seed string `protobuf:"bytes,1,opt,name=seed,proto3" json:"seed,omitempty"`
}
func (x *EncryptionSeed) Reset() {
*x = EncryptionSeed{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *EncryptionSeed) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EncryptionSeed) ProtoMessage() {}
func (x *EncryptionSeed) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_kcp_config_proto_msgTypes[7]
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 EncryptionSeed.ProtoReflect.Descriptor instead.
func (*EncryptionSeed) Descriptor() ([]byte, []int) {
return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{7}
}
func (x *EncryptionSeed) GetSeed() string {
if x != nil {
return x.Seed
}
return ""
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Mtu *MTU `protobuf:"bytes,1,opt,name=mtu,proto3" json:"mtu,omitempty"`
Tti *TTI `protobuf:"bytes,2,opt,name=tti,proto3" json:"tti,omitempty"`
UplinkCapacity *UplinkCapacity `protobuf:"bytes,3,opt,name=uplink_capacity,json=uplinkCapacity,proto3" json:"uplink_capacity,omitempty"`
DownlinkCapacity *DownlinkCapacity `protobuf:"bytes,4,opt,name=downlink_capacity,json=downlinkCapacity,proto3" json:"downlink_capacity,omitempty"`
Congestion bool `protobuf:"varint,5,opt,name=congestion,proto3" json:"congestion,omitempty"`
WriteBuffer *WriteBuffer `protobuf:"bytes,6,opt,name=write_buffer,json=writeBuffer,proto3" json:"write_buffer,omitempty"`
ReadBuffer *ReadBuffer `protobuf:"bytes,7,opt,name=read_buffer,json=readBuffer,proto3" json:"read_buffer,omitempty"`
HeaderConfig *serial.TypedMessage `protobuf:"bytes,8,opt,name=header_config,json=headerConfig,proto3" json:"header_config,omitempty"`
Seed *EncryptionSeed `protobuf:"bytes,10,opt,name=seed,proto3" json:"seed,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_kcp_config_proto_msgTypes[8]
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_kcp_config_proto_msgTypes[8]
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_kcp_config_proto_rawDescGZIP(), []int{8}
}
func (x *Config) GetMtu() *MTU {
if x != nil {
return x.Mtu
}
return nil
}
func (x *Config) GetTti() *TTI {
if x != nil {
return x.Tti
}
return nil
}
func (x *Config) GetUplinkCapacity() *UplinkCapacity {
if x != nil {
return x.UplinkCapacity
}
return nil
}
func (x *Config) GetDownlinkCapacity() *DownlinkCapacity {
if x != nil {
return x.DownlinkCapacity
}
return nil
}
func (x *Config) GetCongestion() bool {
if x != nil {
return x.Congestion
}
return false
}
func (x *Config) GetWriteBuffer() *WriteBuffer {
if x != nil {
return x.WriteBuffer
}
return nil
}
func (x *Config) GetReadBuffer() *ReadBuffer {
if x != nil {
return x.ReadBuffer
}
return nil
}
func (x *Config) GetHeaderConfig() *serial.TypedMessage {
if x != nil {
return x.HeaderConfig
}
return nil
}
func (x *Config) GetSeed() *EncryptionSeed {
if x != nil {
return x.Seed
}
return nil
}
var File_transport_internet_kcp_config_proto protoreflect.FileDescriptor
var file_transport_internet_kcp_config_proto_rawDesc = []byte{
0x0a, 0x23, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x63, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b,
0x63, 0x70, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61,
0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1b, 0x0a, 0x03, 0x4d, 0x54, 0x55, 0x12, 0x14, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x1b, 0x0a, 0x03, 0x54, 0x54, 0x49, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x26, 0x0a, 0x0e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74,
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x28, 0x0a, 0x10, 0x44, 0x6f, 0x77, 0x6e, 0x6c,
0x69, 0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x22, 0x21, 0x0a, 0x0b, 0x57, 0x72, 0x69, 0x74, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72,
0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x42, 0x75, 0x66, 0x66,
0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x29, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x75, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x22, 0x24, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53,
0x65, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x22, 0xe7, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x32, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63, 0x70, 0x2e, 0x4d, 0x54,
0x55, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x32, 0x0a, 0x03, 0x74, 0x74, 0x69, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63,
0x70, 0x2e, 0x54, 0x54, 0x49, 0x52, 0x03, 0x74, 0x74, 0x69, 0x12, 0x54, 0x0a, 0x0f, 0x75, 0x70,
0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63,
0x70, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79,
0x52, 0x0e, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79,
0x12, 0x5a, 0x0a, 0x11, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x63, 0x61, 0x70,
0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63, 0x70, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69,
0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x52, 0x10, 0x64, 0x6f, 0x77, 0x6e,
0x6c, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x0a,
0x63, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0c,
0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63, 0x70,
0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x0b, 0x77, 0x72,
0x69, 0x74, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x0b, 0x72, 0x65, 0x61,
0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x6b, 0x63, 0x70, 0x2e, 0x52, 0x65, 0x61,
0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x42, 0x75, 0x66,
0x66, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0c, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x04, 0x73, 0x65,
0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x2e, 0x6b, 0x63, 0x70, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x53, 0x65, 0x65, 0x64, 0x52, 0x04, 0x73, 0x65, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x09, 0x10,
0x0a, 0x42, 0x76, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x2e, 0x6b, 0x63, 0x70, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x63, 0x70, 0xaa, 0x02, 0x1b, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x4b, 0x63, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_transport_internet_kcp_config_proto_rawDescOnce sync.Once
file_transport_internet_kcp_config_proto_rawDescData = file_transport_internet_kcp_config_proto_rawDesc
)
func file_transport_internet_kcp_config_proto_rawDescGZIP() []byte {
file_transport_internet_kcp_config_proto_rawDescOnce.Do(func() {
file_transport_internet_kcp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_kcp_config_proto_rawDescData)
})
return file_transport_internet_kcp_config_proto_rawDescData
}
var file_transport_internet_kcp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_transport_internet_kcp_config_proto_goTypes = []interface{}{
(*MTU)(nil), // 0: xray.transport.internet.kcp.MTU
(*TTI)(nil), // 1: xray.transport.internet.kcp.TTI
(*UplinkCapacity)(nil), // 2: xray.transport.internet.kcp.UplinkCapacity
(*DownlinkCapacity)(nil), // 3: xray.transport.internet.kcp.DownlinkCapacity
(*WriteBuffer)(nil), // 4: xray.transport.internet.kcp.WriteBuffer
(*ReadBuffer)(nil), // 5: xray.transport.internet.kcp.ReadBuffer
(*ConnectionReuse)(nil), // 6: xray.transport.internet.kcp.ConnectionReuse
(*EncryptionSeed)(nil), // 7: xray.transport.internet.kcp.EncryptionSeed
(*Config)(nil), // 8: xray.transport.internet.kcp.Config
(*serial.TypedMessage)(nil), // 9: xray.common.serial.TypedMessage
}
var file_transport_internet_kcp_config_proto_depIdxs = []int32{
0, // 0: xray.transport.internet.kcp.Config.mtu:type_name -> xray.transport.internet.kcp.MTU
1, // 1: xray.transport.internet.kcp.Config.tti:type_name -> xray.transport.internet.kcp.TTI
2, // 2: xray.transport.internet.kcp.Config.uplink_capacity:type_name -> xray.transport.internet.kcp.UplinkCapacity
3, // 3: xray.transport.internet.kcp.Config.downlink_capacity:type_name -> xray.transport.internet.kcp.DownlinkCapacity
4, // 4: xray.transport.internet.kcp.Config.write_buffer:type_name -> xray.transport.internet.kcp.WriteBuffer
5, // 5: xray.transport.internet.kcp.Config.read_buffer:type_name -> xray.transport.internet.kcp.ReadBuffer
9, // 6: xray.transport.internet.kcp.Config.header_config:type_name -> xray.common.serial.TypedMessage
7, // 7: xray.transport.internet.kcp.Config.seed:type_name -> xray.transport.internet.kcp.EncryptionSeed
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_transport_internet_kcp_config_proto_init() }
func file_transport_internet_kcp_config_proto_init() {
if File_transport_internet_kcp_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_kcp_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MTU); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TTI); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UplinkCapacity); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DownlinkCapacity); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WriteBuffer); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReadBuffer); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ConnectionReuse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EncryptionSeed); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_transport_internet_kcp_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_kcp_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 9,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_kcp_config_proto_goTypes,
DependencyIndexes: file_transport_internet_kcp_config_proto_depIdxs,
MessageInfos: file_transport_internet_kcp_config_proto_msgTypes,
}.Build()
File_transport_internet_kcp_config_proto = out.File
file_transport_internet_kcp_config_proto_rawDesc = nil
file_transport_internet_kcp_config_proto_goTypes = nil
file_transport_internet_kcp_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,61 @@
syntax = "proto3";
package xray.transport.internet.kcp;
option csharp_namespace = "Xray.Transport.Internet.Kcp";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/kcp";
option java_package = "com.xray.transport.internet.kcp";
option java_multiple_files = true;
import "common/serial/typed_message.proto";
// Maximum Transmission Unit, in bytes.
message MTU {
uint32 value = 1;
}
// Transmission Time Interview, in milli-sec.
message TTI {
uint32 value = 1;
}
// Uplink capacity, in MB.
message UplinkCapacity {
uint32 value = 1;
}
// Downlink capacity, in MB.
message DownlinkCapacity {
uint32 value = 1;
}
message WriteBuffer {
// Buffer size in bytes.
uint32 size = 1;
}
message ReadBuffer {
// Buffer size in bytes.
uint32 size = 1;
}
message ConnectionReuse {
bool enable = 1;
}
// Maximum Transmission Unit, in bytes.
message EncryptionSeed {
string seed = 1;
}
message Config {
MTU mtu = 1;
TTI tti = 2;
UplinkCapacity uplink_capacity = 3;
DownlinkCapacity downlink_capacity = 4;
bool congestion = 5;
WriteBuffer write_buffer = 6;
ReadBuffer read_buffer = 7;
xray.common.serial.TypedMessage header_config = 8;
reserved 9;
EncryptionSeed seed = 10;
}

View file

@ -0,0 +1,663 @@
// +build !confonly
package kcp
import (
"bytes"
"io"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/signal"
"github.com/xtls/xray-core/v1/common/signal/semaphore"
)
var (
ErrIOTimeout = newError("Read/Write timeout")
ErrClosedListener = newError("Listener closed.")
ErrClosedConnection = newError("Connection closed.")
)
// State of the connection
type State int32
// Is returns true if current State is one of the candidates.
func (s State) Is(states ...State) bool {
for _, state := range states {
if s == state {
return true
}
}
return false
}
const (
StateActive State = 0 // Connection is active
StateReadyToClose State = 1 // Connection is closed locally
StatePeerClosed State = 2 // Connection is closed on remote
StateTerminating State = 3 // Connection is ready to be destroyed locally
StatePeerTerminating State = 4 // Connection is ready to be destroyed on remote
StateTerminated State = 5 // Connection is destroyed.
)
func nowMillisec() int64 {
now := time.Now()
return now.Unix()*1000 + int64(now.Nanosecond()/1000000)
}
type RoundTripInfo struct {
sync.RWMutex
variation uint32
srtt uint32
rto uint32
minRtt uint32
updatedTimestamp uint32
}
func (info *RoundTripInfo) UpdatePeerRTO(rto uint32, current uint32) {
info.Lock()
defer info.Unlock()
if current-info.updatedTimestamp < 3000 {
return
}
info.updatedTimestamp = current
info.rto = rto
}
func (info *RoundTripInfo) Update(rtt uint32, current uint32) {
if rtt > 0x7FFFFFFF {
return
}
info.Lock()
defer info.Unlock()
// https://tools.ietf.org/html/rfc6298
if info.srtt == 0 {
info.srtt = rtt
info.variation = rtt / 2
} else {
delta := rtt - info.srtt
if info.srtt > rtt {
delta = info.srtt - rtt
}
info.variation = (3*info.variation + delta) / 4
info.srtt = (7*info.srtt + rtt) / 8
if info.srtt < info.minRtt {
info.srtt = info.minRtt
}
}
var rto uint32
if info.minRtt < 4*info.variation {
rto = info.srtt + 4*info.variation
} else {
rto = info.srtt + info.variation
}
if rto > 10000 {
rto = 10000
}
info.rto = rto * 5 / 4
info.updatedTimestamp = current
}
func (info *RoundTripInfo) Timeout() uint32 {
info.RLock()
defer info.RUnlock()
return info.rto
}
func (info *RoundTripInfo) SmoothedTime() uint32 {
info.RLock()
defer info.RUnlock()
return info.srtt
}
type Updater struct {
interval int64
shouldContinue func() bool
shouldTerminate func() bool
updateFunc func()
notifier *semaphore.Instance
}
func NewUpdater(interval uint32, shouldContinue func() bool, shouldTerminate func() bool, updateFunc func()) *Updater {
u := &Updater{
interval: int64(time.Duration(interval) * time.Millisecond),
shouldContinue: shouldContinue,
shouldTerminate: shouldTerminate,
updateFunc: updateFunc,
notifier: semaphore.New(1),
}
return u
}
func (u *Updater) WakeUp() {
select {
case <-u.notifier.Wait():
go u.run()
default:
}
}
func (u *Updater) run() {
defer u.notifier.Signal()
if u.shouldTerminate() {
return
}
ticker := time.NewTicker(u.Interval())
for u.shouldContinue() {
u.updateFunc()
<-ticker.C
}
ticker.Stop()
}
func (u *Updater) Interval() time.Duration {
return time.Duration(atomic.LoadInt64(&u.interval))
}
func (u *Updater) SetInterval(d time.Duration) {
atomic.StoreInt64(&u.interval, int64(d))
}
type ConnMetadata struct {
LocalAddr net.Addr
RemoteAddr net.Addr
Conversation uint16
}
// Connection is a KCP connection over UDP.
type Connection struct {
meta ConnMetadata
closer io.Closer
rd time.Time
wd time.Time // write deadline
since int64
dataInput *signal.Notifier
dataOutput *signal.Notifier
Config *Config
state State
stateBeginTime uint32
lastIncomingTime uint32
lastPingTime uint32
mss uint32
roundTrip *RoundTripInfo
receivingWorker *ReceivingWorker
sendingWorker *SendingWorker
output SegmentWriter
dataUpdater *Updater
pingUpdater *Updater
}
// NewConnection create a new KCP connection between local and remote.
func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, config *Config) *Connection {
newError("#", meta.Conversation, " creating connection to ", meta.RemoteAddr).WriteToLog()
conn := &Connection{
meta: meta,
closer: closer,
since: nowMillisec(),
dataInput: signal.NewNotifier(),
dataOutput: signal.NewNotifier(),
Config: config,
output: NewRetryableWriter(NewSegmentWriter(writer)),
mss: config.GetMTUValue() - uint32(writer.Overhead()) - DataSegmentOverhead,
roundTrip: &RoundTripInfo{
rto: 100,
minRtt: config.GetTTIValue(),
},
}
conn.receivingWorker = NewReceivingWorker(conn)
conn.sendingWorker = NewSendingWorker(conn)
isTerminating := func() bool {
return conn.State().Is(StateTerminating, StateTerminated)
}
isTerminated := func() bool {
return conn.State() == StateTerminated
}
conn.dataUpdater = NewUpdater(
config.GetTTIValue(),
func() bool {
return !isTerminating() && (conn.sendingWorker.UpdateNecessary() || conn.receivingWorker.UpdateNecessary())
},
isTerminating,
conn.updateTask)
conn.pingUpdater = NewUpdater(
5000, // 5 seconds
func() bool { return !isTerminated() },
isTerminated,
conn.updateTask)
conn.pingUpdater.WakeUp()
return conn
}
func (c *Connection) Elapsed() uint32 {
return uint32(nowMillisec() - c.since)
}
// ReadMultiBuffer implements buf.Reader.
func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) {
if c == nil {
return nil, io.EOF
}
for {
if c.State().Is(StateReadyToClose, StateTerminating, StateTerminated) {
return nil, io.EOF
}
mb := c.receivingWorker.ReadMultiBuffer()
if !mb.IsEmpty() {
c.dataUpdater.WakeUp()
return mb, nil
}
if c.State() == StatePeerTerminating {
return nil, io.EOF
}
if err := c.waitForDataInput(); err != nil {
return nil, err
}
}
}
func (c *Connection) waitForDataInput() error {
for i := 0; i < 16; i++ {
select {
case <-c.dataInput.Wait():
return nil
default:
runtime.Gosched()
}
}
duration := time.Second * 16
if !c.rd.IsZero() {
duration = time.Until(c.rd)
if duration < 0 {
return ErrIOTimeout
}
}
timeout := time.NewTimer(duration)
defer timeout.Stop()
select {
case <-c.dataInput.Wait():
case <-timeout.C:
if !c.rd.IsZero() && c.rd.Before(time.Now()) {
return ErrIOTimeout
}
}
return nil
}
// Read implements the Conn Read method.
func (c *Connection) Read(b []byte) (int, error) {
if c == nil {
return 0, io.EOF
}
for {
if c.State().Is(StateReadyToClose, StateTerminating, StateTerminated) {
return 0, io.EOF
}
nBytes := c.receivingWorker.Read(b)
if nBytes > 0 {
c.dataUpdater.WakeUp()
return nBytes, nil
}
if err := c.waitForDataInput(); err != nil {
return 0, err
}
}
}
func (c *Connection) waitForDataOutput() error {
for i := 0; i < 16; i++ {
select {
case <-c.dataOutput.Wait():
return nil
default:
runtime.Gosched()
}
}
duration := time.Second * 16
if !c.wd.IsZero() {
duration = time.Until(c.wd)
if duration < 0 {
return ErrIOTimeout
}
}
timeout := time.NewTimer(duration)
defer timeout.Stop()
select {
case <-c.dataOutput.Wait():
case <-timeout.C:
if !c.wd.IsZero() && c.wd.Before(time.Now()) {
return ErrIOTimeout
}
}
return nil
}
// Write implements io.Writer.
func (c *Connection) Write(b []byte) (int, error) {
reader := bytes.NewReader(b)
if err := c.writeMultiBufferInternal(reader); err != nil {
return 0, err
}
return len(b), nil
}
// WriteMultiBuffer implements buf.Writer.
func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
reader := &buf.MultiBufferContainer{
MultiBuffer: mb,
}
defer reader.Close()
return c.writeMultiBufferInternal(reader)
}
func (c *Connection) writeMultiBufferInternal(reader io.Reader) error {
updatePending := false
defer func() {
if updatePending {
c.dataUpdater.WakeUp()
}
}()
var b *buf.Buffer
defer b.Release()
for {
for {
if c == nil || c.State() != StateActive {
return io.ErrClosedPipe
}
if b == nil {
b = buf.New()
_, err := b.ReadFrom(io.LimitReader(reader, int64(c.mss)))
if err != nil {
return nil
}
}
if !c.sendingWorker.Push(b) {
break
}
updatePending = true
b = nil
}
if updatePending {
c.dataUpdater.WakeUp()
updatePending = false
}
if err := c.waitForDataOutput(); err != nil {
return err
}
}
}
func (c *Connection) SetState(state State) {
current := c.Elapsed()
atomic.StoreInt32((*int32)(&c.state), int32(state))
atomic.StoreUint32(&c.stateBeginTime, current)
newError("#", c.meta.Conversation, " entering state ", state, " at ", current).AtDebug().WriteToLog()
switch state {
case StateReadyToClose:
c.receivingWorker.CloseRead()
case StatePeerClosed:
c.sendingWorker.CloseWrite()
case StateTerminating:
c.receivingWorker.CloseRead()
c.sendingWorker.CloseWrite()
c.pingUpdater.SetInterval(time.Second)
case StatePeerTerminating:
c.sendingWorker.CloseWrite()
c.pingUpdater.SetInterval(time.Second)
case StateTerminated:
c.receivingWorker.CloseRead()
c.sendingWorker.CloseWrite()
c.pingUpdater.SetInterval(time.Second)
c.dataUpdater.WakeUp()
c.pingUpdater.WakeUp()
go c.Terminate()
}
}
// Close closes the connection.
func (c *Connection) Close() error {
if c == nil {
return ErrClosedConnection
}
c.dataInput.Signal()
c.dataOutput.Signal()
switch c.State() {
case StateReadyToClose, StateTerminating, StateTerminated:
return ErrClosedConnection
case StateActive:
c.SetState(StateReadyToClose)
case StatePeerClosed:
c.SetState(StateTerminating)
case StatePeerTerminating:
c.SetState(StateTerminated)
}
newError("#", c.meta.Conversation, " closing connection to ", c.meta.RemoteAddr).WriteToLog()
return nil
}
// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it.
func (c *Connection) LocalAddr() net.Addr {
if c == nil {
return nil
}
return c.meta.LocalAddr
}
// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
func (c *Connection) RemoteAddr() net.Addr {
if c == nil {
return nil
}
return c.meta.RemoteAddr
}
// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
func (c *Connection) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *Connection) SetReadDeadline(t time.Time) error {
if c == nil || c.State() != StateActive {
return ErrClosedConnection
}
c.rd = t
return nil
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *Connection) SetWriteDeadline(t time.Time) error {
if c == nil || c.State() != StateActive {
return ErrClosedConnection
}
c.wd = t
return nil
}
// kcp update, input loop
func (c *Connection) updateTask() {
c.flush()
}
func (c *Connection) Terminate() {
if c == nil {
return
}
newError("#", c.meta.Conversation, " terminating connection to ", c.RemoteAddr()).WriteToLog()
// v.SetState(StateTerminated)
c.dataInput.Signal()
c.dataOutput.Signal()
c.closer.Close()
c.sendingWorker.Release()
c.receivingWorker.Release()
}
func (c *Connection) HandleOption(opt SegmentOption) {
if (opt & SegmentOptionClose) == SegmentOptionClose {
c.OnPeerClosed()
}
}
func (c *Connection) OnPeerClosed() {
switch c.State() {
case StateReadyToClose:
c.SetState(StateTerminating)
case StateActive:
c.SetState(StatePeerClosed)
}
}
// Input when you received a low level packet (eg. UDP packet), call it
func (c *Connection) Input(segments []Segment) {
current := c.Elapsed()
atomic.StoreUint32(&c.lastIncomingTime, current)
for _, seg := range segments {
if seg.Conversation() != c.meta.Conversation {
break
}
switch seg := seg.(type) {
case *DataSegment:
c.HandleOption(seg.Option)
c.receivingWorker.ProcessSegment(seg)
if c.receivingWorker.IsDataAvailable() {
c.dataInput.Signal()
}
c.dataUpdater.WakeUp()
case *AckSegment:
c.HandleOption(seg.Option)
c.sendingWorker.ProcessSegment(current, seg, c.roundTrip.Timeout())
c.dataOutput.Signal()
c.dataUpdater.WakeUp()
case *CmdOnlySegment:
c.HandleOption(seg.Option)
if seg.Command() == CommandTerminate {
switch c.State() {
case StateActive, StatePeerClosed:
c.SetState(StatePeerTerminating)
case StateReadyToClose:
c.SetState(StateTerminating)
case StateTerminating:
c.SetState(StateTerminated)
}
}
if seg.Option == SegmentOptionClose || seg.Command() == CommandTerminate {
c.dataInput.Signal()
c.dataOutput.Signal()
}
c.sendingWorker.ProcessReceivingNext(seg.ReceivingNext)
c.receivingWorker.ProcessSendingNext(seg.SendingNext)
c.roundTrip.UpdatePeerRTO(seg.PeerRTO, current)
seg.Release()
default:
}
}
}
func (c *Connection) flush() {
current := c.Elapsed()
if c.State() == StateTerminated {
return
}
if c.State() == StateActive && current-atomic.LoadUint32(&c.lastIncomingTime) >= 30000 {
c.Close()
}
if c.State() == StateReadyToClose && c.sendingWorker.IsEmpty() {
c.SetState(StateTerminating)
}
if c.State() == StateTerminating {
newError("#", c.meta.Conversation, " sending terminating cmd.").AtDebug().WriteToLog()
c.Ping(current, CommandTerminate)
if current-atomic.LoadUint32(&c.stateBeginTime) > 8000 {
c.SetState(StateTerminated)
}
return
}
if c.State() == StatePeerTerminating && current-atomic.LoadUint32(&c.stateBeginTime) > 4000 {
c.SetState(StateTerminating)
}
if c.State() == StateReadyToClose && current-atomic.LoadUint32(&c.stateBeginTime) > 15000 {
c.SetState(StateTerminating)
}
// flush acknowledges
c.receivingWorker.Flush(current)
c.sendingWorker.Flush(current)
if current-atomic.LoadUint32(&c.lastPingTime) >= 3000 {
c.Ping(current, CommandPing)
}
}
func (c *Connection) State() State {
return State(atomic.LoadInt32((*int32)(&c.state)))
}
func (c *Connection) Ping(current uint32, cmd Command) {
seg := NewCmdOnlySegment()
seg.Conv = c.meta.Conversation
seg.Cmd = cmd
seg.ReceivingNext = c.receivingWorker.NextNumber()
seg.SendingNext = c.sendingWorker.FirstUnacknowledged()
seg.PeerRTO = c.roundTrip.Timeout()
if c.State() == StateReadyToClose {
seg.Option = SegmentOptionClose
}
c.output.Write(seg)
atomic.StoreUint32(&c.lastPingTime, current)
seg.Release()
}

View file

@ -0,0 +1,38 @@
package kcp_test
import (
"io"
"testing"
"time"
"github.com/xtls/xray-core/v1/common/buf"
. "github.com/xtls/xray-core/v1/transport/internet/kcp"
)
type NoOpCloser int
func (NoOpCloser) Close() error {
return nil
}
func TestConnectionReadTimeout(t *testing.T) {
conn := NewConnection(ConnMetadata{Conversation: 1}, &KCPPacketWriter{
Writer: buf.DiscardBytes,
}, NoOpCloser(0), &Config{})
conn.SetReadDeadline(time.Now().Add(time.Second))
b := make([]byte, 1024)
nBytes, err := conn.Read(b)
if nBytes != 0 || err == nil {
t.Error("unexpected read: ", nBytes, err)
}
conn.Terminate()
}
func TestConnectionInterface(t *testing.T) {
_ = (io.Writer)(new(Connection))
_ = (io.Reader)(new(Connection))
_ = (buf.Reader)(new(Connection))
_ = (buf.Writer)(new(Connection))
}

View file

@ -0,0 +1,78 @@
// +build !confonly
package kcp
import (
"crypto/cipher"
"encoding/binary"
"hash/fnv"
"github.com/xtls/xray-core/v1/common"
)
// SimpleAuthenticator is a legacy AEAD used for KCP encryption.
type SimpleAuthenticator struct{}
// NewSimpleAuthenticator creates a new SimpleAuthenticator
func NewSimpleAuthenticator() cipher.AEAD {
return &SimpleAuthenticator{}
}
// NonceSize implements cipher.AEAD.NonceSize().
func (*SimpleAuthenticator) NonceSize() int {
return 0
}
// Overhead implements cipher.AEAD.NonceSize().
func (*SimpleAuthenticator) Overhead() int {
return 6
}
// Seal implements cipher.AEAD.Seal().
func (a *SimpleAuthenticator) Seal(dst, nonce, plain, extra []byte) []byte {
dst = append(dst, 0, 0, 0, 0, 0, 0) // 4 bytes for hash, and then 2 bytes for length
binary.BigEndian.PutUint16(dst[4:], uint16(len(plain)))
dst = append(dst, plain...)
fnvHash := fnv.New32a()
common.Must2(fnvHash.Write(dst[4:]))
fnvHash.Sum(dst[:0])
dstLen := len(dst)
xtra := 4 - dstLen%4
if xtra != 4 {
dst = append(dst, make([]byte, xtra)...)
}
xorfwd(dst)
if xtra != 4 {
dst = dst[:dstLen]
}
return dst
}
// Open implements cipher.AEAD.Open().
func (a *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) {
dst = append(dst, cipherText...)
dstLen := len(dst)
xtra := 4 - dstLen%4
if xtra != 4 {
dst = append(dst, make([]byte, xtra)...)
}
xorbkd(dst)
if xtra != 4 {
dst = dst[:dstLen]
}
fnvHash := fnv.New32a()
common.Must2(fnvHash.Write(dst[4:]))
if binary.BigEndian.Uint32(dst[:4]) != fnvHash.Sum32() {
return nil, newError("invalid auth")
}
length := binary.BigEndian.Uint16(dst[4:6])
if len(dst)-6 != int(length) {
return nil, newError("invalid auth")
}
return dst[6:], nil
}

View file

@ -0,0 +1,38 @@
package kcp_test
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/v1/common"
. "github.com/xtls/xray-core/v1/transport/internet/kcp"
)
func TestSimpleAuthenticator(t *testing.T) {
cache := make([]byte, 512)
payload := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
auth := NewSimpleAuthenticator()
b := auth.Seal(cache[:0], nil, payload, nil)
c, err := auth.Open(cache[:0], nil, b, nil)
common.Must(err)
if r := cmp.Diff(c, payload); r != "" {
t.Error(r)
}
}
func TestSimpleAuthenticator2(t *testing.T) {
cache := make([]byte, 512)
payload := []byte{'a', 'b'}
auth := NewSimpleAuthenticator()
b := auth.Seal(cache[:0], nil, payload, nil)
c, err := auth.Open(cache[:0], nil, b, nil)
common.Must(err)
if r := cmp.Diff(c, payload); r != "" {
t.Error(r)
}
}

View file

@ -0,0 +1,15 @@
package kcp
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"github.com/xtls/xray-core/v1/common"
)
func NewAEADAESGCMBasedOnSeed(seed string) cipher.AEAD {
hashedSeed := sha256.Sum256([]byte(seed))
aesBlock := common.Must2(aes.NewCipher(hashedSeed[:16])).(cipher.Block)
return common.Must2(cipher.NewGCM(aesBlock)).(cipher.AEAD)
}

View file

@ -0,0 +1,102 @@
// +build !confonly
package kcp
import (
"context"
"io"
"sync/atomic"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/dice"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
"github.com/xtls/xray-core/v1/transport/internet/xtls"
)
var (
globalConv = uint32(dice.RollUint16())
)
func fetchInput(_ context.Context, input io.Reader, reader PacketReader, conn *Connection) {
cache := make(chan *buf.Buffer, 1024)
go func() {
for {
payload := buf.New()
if _, err := payload.ReadFrom(input); err != nil {
payload.Release()
close(cache)
return
}
select {
case cache <- payload:
default:
payload.Release()
}
}
}()
for payload := range cache {
segments := reader.Read(payload.Bytes())
payload.Release()
if len(segments) > 0 {
conn.Input(segments)
}
}
}
// DialKCP dials a new KCP connections to the specific destination.
func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
dest.Network = net.Network_UDP
newError("dialing mKCP to ", dest).WriteToLog()
rawConn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil {
return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err)
}
kcpSettings := streamSettings.ProtocolSettings.(*Config)
header, err := kcpSettings.GetPackerHeader()
if err != nil {
return nil, newError("failed to create packet header").Base(err)
}
security, err := kcpSettings.GetSecurity()
if err != nil {
return nil, newError("failed to create security").Base(err)
}
reader := &KCPPacketReader{
Header: header,
Security: security,
}
writer := &KCPPacketWriter{
Header: header,
Security: security,
Writer: rawConn,
}
conv := uint16(atomic.AddUint32(&globalConv, 1))
session := NewConnection(ConnMetadata{
LocalAddr: rawConn.LocalAddr(),
RemoteAddr: rawConn.RemoteAddr(),
Conversation: conv,
}, writer, rawConn, kcpSettings)
go fetchInput(ctx, rawConn, reader, session)
var iConn internet.Connection = session
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
iConn = tls.Client(iConn, config.GetTLSConfig(tls.WithDestination(dest)))
} else if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
iConn = xtls.Client(iConn, config.GetXTLSConfig(xtls.WithDestination(dest)))
}
return iConn, nil
}
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, DialKCP))
}

View file

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

View file

@ -0,0 +1,97 @@
// +build !confonly
package kcp
import (
"crypto/cipher"
"crypto/rand"
"io"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/transport/internet"
)
type PacketReader interface {
Read([]byte) []Segment
}
type PacketWriter interface {
Overhead() int
io.Writer
}
type KCPPacketReader struct {
Security cipher.AEAD
Header internet.PacketHeader
}
func (r *KCPPacketReader) Read(b []byte) []Segment {
if r.Header != nil {
if int32(len(b)) <= r.Header.Size() {
return nil
}
b = b[r.Header.Size():]
}
if r.Security != nil {
nonceSize := r.Security.NonceSize()
overhead := r.Security.Overhead()
if len(b) <= nonceSize+overhead {
return nil
}
out, err := r.Security.Open(b[nonceSize:nonceSize], b[:nonceSize], b[nonceSize:], nil)
if err != nil {
return nil
}
b = out
}
var result []Segment
for len(b) > 0 {
seg, x := ReadSegment(b)
if seg == nil {
break
}
result = append(result, seg)
b = x
}
return result
}
type KCPPacketWriter struct {
Header internet.PacketHeader
Security cipher.AEAD
Writer io.Writer
}
func (w *KCPPacketWriter) Overhead() int {
overhead := 0
if w.Header != nil {
overhead += int(w.Header.Size())
}
if w.Security != nil {
overhead += w.Security.Overhead()
}
return overhead
}
func (w *KCPPacketWriter) Write(b []byte) (int, error) {
bb := buf.StackNew()
defer bb.Release()
if w.Header != nil {
w.Header.Serialize(bb.Extend(w.Header.Size()))
}
if w.Security != nil {
nonceSize := w.Security.NonceSize()
common.Must2(bb.ReadFullFrom(rand.Reader, int32(nonceSize)))
nonce := bb.BytesFrom(int32(-nonceSize))
encrypted := bb.Extend(int32(w.Security.Overhead() + len(b)))
w.Security.Seal(encrypted[:0], nonce, b, nil)
} else {
bb.Write(b)
}
_, err := w.Writer.Write(bb.Bytes())
return len(b), err
}

View file

@ -0,0 +1,36 @@
package kcp_test
import (
"testing"
. "github.com/xtls/xray-core/v1/transport/internet/kcp"
)
func TestKCPPacketReader(t *testing.T) {
reader := KCPPacketReader{
Security: &SimpleAuthenticator{},
}
testCases := []struct {
Input []byte
Output []Segment
}{
{
Input: []byte{},
Output: nil,
},
{
Input: []byte{1},
Output: nil,
},
}
for _, testCase := range testCases {
seg := reader.Read(testCase.Input)
if testCase.Output == nil && seg != nil {
t.Errorf("Expect nothing returned, but actually %v", seg)
} else if testCase.Output != nil && seg == nil {
t.Errorf("Expect some output, but got nil")
}
}
}

View file

@ -0,0 +1,8 @@
// Package kcp - A Fast and Reliable ARQ Protocol
//
// Acknowledgement:
// skywind3000@github for inventing the KCP protocol
// xtaci@github for translating to Golang
package kcp
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen

View file

@ -0,0 +1,85 @@
package kcp_test
import (
"context"
"crypto/rand"
"io"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"golang.org/x/sync/errgroup"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/errors"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
. "github.com/xtls/xray-core/v1/transport/internet/kcp"
)
func TestDialAndListen(t *testing.T) {
listerner, err := NewListener(context.Background(), net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{
ProtocolName: "mkcp",
ProtocolSettings: &Config{},
}, func(conn internet.Connection) {
go func(c internet.Connection) {
payload := make([]byte, 4096)
for {
nBytes, err := c.Read(payload)
if err != nil {
break
}
for idx, b := range payload[:nBytes] {
payload[idx] = b ^ 'c'
}
c.Write(payload[:nBytes])
}
c.Close()
}(conn)
})
common.Must(err)
defer listerner.Close()
port := net.Port(listerner.Addr().(*net.UDPAddr).Port)
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{
ProtocolName: "mkcp",
ProtocolSettings: &Config{},
})
if err != nil {
return err
}
defer clientConn.Close()
clientSend := make([]byte, 1024*1024)
rand.Read(clientSend)
go clientConn.Write(clientSend)
clientReceived := make([]byte, 1024*1024)
common.Must2(io.ReadFull(clientConn, clientReceived))
clientExpected := make([]byte, 1024*1024)
for idx, b := range clientSend {
clientExpected[idx] = b ^ 'c'
}
if r := cmp.Diff(clientReceived, clientExpected); r != "" {
return errors.New(r)
}
return nil
})
}
if err := errg.Wait(); err != nil {
t.Fatal(err)
}
for i := 0; i < 60 && listerner.ActiveConnections() > 0; i++ {
time.Sleep(500 * time.Millisecond)
}
if v := listerner.ActiveConnections(); v != 0 {
t.Error("active connections: ", v)
}
}

View file

@ -0,0 +1,206 @@
// +build !confonly
package kcp
import (
"context"
"crypto/cipher"
gotls "crypto/tls"
"sync"
goxtls "github.com/xtls/go"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
"github.com/xtls/xray-core/v1/transport/internet/udp"
"github.com/xtls/xray-core/v1/transport/internet/xtls"
)
type ConnectionID struct {
Remote net.Address
Port net.Port
Conv uint16
}
// Listener defines a server listening for connections
type Listener struct {
sync.Mutex
sessions map[ConnectionID]*Connection
hub *udp.Hub
tlsConfig *gotls.Config
xtlsConfig *goxtls.Config
config *Config
reader PacketReader
header internet.PacketHeader
security cipher.AEAD
addConn internet.ConnHandler
}
func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) {
kcpSettings := streamSettings.ProtocolSettings.(*Config)
header, err := kcpSettings.GetPackerHeader()
if err != nil {
return nil, newError("failed to create packet header").Base(err).AtError()
}
security, err := kcpSettings.GetSecurity()
if err != nil {
return nil, newError("failed to create security").Base(err).AtError()
}
l := &Listener{
header: header,
security: security,
reader: &KCPPacketReader{
Header: header,
Security: security,
},
sessions: make(map[ConnectionID]*Connection),
config: kcpSettings,
addConn: addConn,
}
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
l.tlsConfig = config.GetTLSConfig()
}
if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
l.xtlsConfig = config.GetXTLSConfig()
}
hub, err := udp.ListenUDP(ctx, address, port, streamSettings, udp.HubCapacity(1024))
if err != nil {
return nil, err
}
l.Lock()
l.hub = hub
l.Unlock()
newError("listening on ", address, ":", port).WriteToLog()
go l.handlePackets()
return l, nil
}
func (l *Listener) handlePackets() {
receive := l.hub.Receive()
for payload := range receive {
l.OnReceive(payload.Payload, payload.Source)
}
}
func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) {
segments := l.reader.Read(payload.Bytes())
payload.Release()
if len(segments) == 0 {
newError("discarding invalid payload from ", src).WriteToLog()
return
}
conv := segments[0].Conversation()
cmd := segments[0].Command()
id := ConnectionID{
Remote: src.Address,
Port: src.Port,
Conv: conv,
}
l.Lock()
defer l.Unlock()
conn, found := l.sessions[id]
if !found {
if cmd == CommandTerminate {
return
}
writer := &Writer{
id: id,
hub: l.hub,
dest: src,
listener: l,
}
remoteAddr := &net.UDPAddr{
IP: src.Address.IP(),
Port: int(src.Port),
}
localAddr := l.hub.Addr()
conn = NewConnection(ConnMetadata{
LocalAddr: localAddr,
RemoteAddr: remoteAddr,
Conversation: conv,
}, &KCPPacketWriter{
Header: l.header,
Security: l.security,
Writer: writer,
}, writer, l.config)
var netConn internet.Connection = conn
if l.tlsConfig != nil {
netConn = tls.Server(conn, l.tlsConfig)
} else if l.xtlsConfig != nil {
netConn = xtls.Server(conn, l.xtlsConfig)
}
l.addConn(netConn)
l.sessions[id] = conn
}
conn.Input(segments)
}
func (l *Listener) Remove(id ConnectionID) {
l.Lock()
delete(l.sessions, id)
l.Unlock()
}
// Close stops listening on the UDP address. Already Accepted connections are not closed.
func (l *Listener) Close() error {
l.hub.Close()
l.Lock()
defer l.Unlock()
for _, conn := range l.sessions {
go conn.Terminate()
}
return nil
}
func (l *Listener) ActiveConnections() int {
l.Lock()
defer l.Unlock()
return len(l.sessions)
}
// Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it.
func (l *Listener) Addr() net.Addr {
return l.hub.Addr()
}
type Writer struct {
id ConnectionID
dest net.Destination
hub *udp.Hub
listener *Listener
}
func (w *Writer) Write(payload []byte) (int, error) {
return w.hub.WriteTo(payload, w.dest)
}
func (w *Writer) Close() error {
w.listener.Remove(w.id)
return nil
}
func ListenKCP(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, ListenKCP))
}

View file

@ -0,0 +1,56 @@
// +build !confonly
package kcp
import (
"io"
"sync"
"github.com/xtls/xray-core/v1/common/retry"
"github.com/xtls/xray-core/v1/common/buf"
)
type SegmentWriter interface {
Write(seg Segment) error
}
type SimpleSegmentWriter struct {
sync.Mutex
buffer *buf.Buffer
writer io.Writer
}
func NewSegmentWriter(writer io.Writer) SegmentWriter {
return &SimpleSegmentWriter{
writer: writer,
buffer: buf.New(),
}
}
func (w *SimpleSegmentWriter) Write(seg Segment) error {
w.Lock()
defer w.Unlock()
w.buffer.Clear()
rawBytes := w.buffer.Extend(seg.ByteSize())
seg.Serialize(rawBytes)
_, err := w.writer.Write(w.buffer.Bytes())
return err
}
type RetryableWriter struct {
writer SegmentWriter
}
func NewRetryableWriter(writer SegmentWriter) SegmentWriter {
return &RetryableWriter{
writer: writer,
}
}
func (w *RetryableWriter) Write(seg Segment) error {
return retry.Timed(5, 100).On(func() error {
return w.writer.Write(seg)
})
}

View file

@ -0,0 +1,260 @@
// +build !confonly
package kcp
import (
"sync"
"github.com/xtls/xray-core/v1/common/buf"
)
type ReceivingWindow struct {
cache map[uint32]*DataSegment
}
func NewReceivingWindow() *ReceivingWindow {
return &ReceivingWindow{
cache: make(map[uint32]*DataSegment),
}
}
func (w *ReceivingWindow) Set(id uint32, value *DataSegment) bool {
_, f := w.cache[id]
if f {
return false
}
w.cache[id] = value
return true
}
func (w *ReceivingWindow) Has(id uint32) bool {
_, f := w.cache[id]
return f
}
func (w *ReceivingWindow) Remove(id uint32) *DataSegment {
v, f := w.cache[id]
if !f {
return nil
}
delete(w.cache, id)
return v
}
type AckList struct {
writer SegmentWriter
timestamps []uint32
numbers []uint32
nextFlush []uint32
flushCandidates []uint32
dirty bool
}
func NewAckList(writer SegmentWriter) *AckList {
return &AckList{
writer: writer,
timestamps: make([]uint32, 0, 128),
numbers: make([]uint32, 0, 128),
nextFlush: make([]uint32, 0, 128),
flushCandidates: make([]uint32, 0, 128),
}
}
func (l *AckList) Add(number uint32, timestamp uint32) {
l.timestamps = append(l.timestamps, timestamp)
l.numbers = append(l.numbers, number)
l.nextFlush = append(l.nextFlush, 0)
l.dirty = true
}
func (l *AckList) Clear(una uint32) {
count := 0
for i := 0; i < len(l.numbers); i++ {
if l.numbers[i] < una {
continue
}
if i != count {
l.numbers[count] = l.numbers[i]
l.timestamps[count] = l.timestamps[i]
l.nextFlush[count] = l.nextFlush[i]
}
count++
}
if count < len(l.numbers) {
l.numbers = l.numbers[:count]
l.timestamps = l.timestamps[:count]
l.nextFlush = l.nextFlush[:count]
l.dirty = true
}
}
func (l *AckList) Flush(current uint32, rto uint32) {
l.flushCandidates = l.flushCandidates[:0]
seg := NewAckSegment()
for i := 0; i < len(l.numbers); i++ {
if l.nextFlush[i] > current {
if len(l.flushCandidates) < cap(l.flushCandidates) {
l.flushCandidates = append(l.flushCandidates, l.numbers[i])
}
continue
}
seg.PutNumber(l.numbers[i])
seg.PutTimestamp(l.timestamps[i])
timeout := rto / 2
if timeout < 20 {
timeout = 20
}
l.nextFlush[i] = current + timeout
if seg.IsFull() {
l.writer.Write(seg)
seg.Release()
seg = NewAckSegment()
l.dirty = false
}
}
if l.dirty || !seg.IsEmpty() {
for _, number := range l.flushCandidates {
if seg.IsFull() {
break
}
seg.PutNumber(number)
}
l.writer.Write(seg)
l.dirty = false
}
seg.Release()
}
type ReceivingWorker struct {
sync.RWMutex
conn *Connection
leftOver buf.MultiBuffer
window *ReceivingWindow
acklist *AckList
nextNumber uint32
windowSize uint32
}
func NewReceivingWorker(kcp *Connection) *ReceivingWorker {
worker := &ReceivingWorker{
conn: kcp,
window: NewReceivingWindow(),
windowSize: kcp.Config.GetReceivingInFlightSize(),
}
worker.acklist = NewAckList(worker)
return worker
}
func (w *ReceivingWorker) Release() {
w.Lock()
buf.ReleaseMulti(w.leftOver)
w.leftOver = nil
w.Unlock()
}
func (w *ReceivingWorker) ProcessSendingNext(number uint32) {
w.Lock()
defer w.Unlock()
w.acklist.Clear(number)
}
func (w *ReceivingWorker) ProcessSegment(seg *DataSegment) {
w.Lock()
defer w.Unlock()
number := seg.Number
idx := number - w.nextNumber
if idx >= w.windowSize {
return
}
w.acklist.Clear(seg.SendingNext)
w.acklist.Add(number, seg.Timestamp)
if !w.window.Set(seg.Number, seg) {
seg.Release()
}
}
func (w *ReceivingWorker) ReadMultiBuffer() buf.MultiBuffer {
if w.leftOver != nil {
mb := w.leftOver
w.leftOver = nil
return mb
}
mb := make(buf.MultiBuffer, 0, 32)
w.Lock()
defer w.Unlock()
for {
seg := w.window.Remove(w.nextNumber)
if seg == nil {
break
}
w.nextNumber++
mb = append(mb, seg.Detach())
seg.Release()
}
return mb
}
func (w *ReceivingWorker) Read(b []byte) int {
mb := w.ReadMultiBuffer()
if mb.IsEmpty() {
return 0
}
mb, nBytes := buf.SplitBytes(mb, b)
if !mb.IsEmpty() {
w.leftOver = mb
}
return nBytes
}
func (w *ReceivingWorker) IsDataAvailable() bool {
w.RLock()
defer w.RUnlock()
return w.window.Has(w.nextNumber)
}
func (w *ReceivingWorker) NextNumber() uint32 {
w.RLock()
defer w.RUnlock()
return w.nextNumber
}
func (w *ReceivingWorker) Flush(current uint32) {
w.Lock()
defer w.Unlock()
w.acklist.Flush(current, w.conn.roundTrip.Timeout())
}
func (w *ReceivingWorker) Write(seg Segment) error {
ackSeg := seg.(*AckSegment)
ackSeg.Conv = w.conn.meta.Conversation
ackSeg.ReceivingNext = w.nextNumber
ackSeg.ReceivingWindow = w.nextNumber + w.windowSize
ackSeg.Option = 0
if w.conn.State() == StateReadyToClose {
ackSeg.Option = SegmentOptionClose
}
return w.conn.output.Write(ackSeg)
}
func (*ReceivingWorker) CloseRead() {
}
func (w *ReceivingWorker) UpdateNecessary() bool {
w.RLock()
defer w.RUnlock()
return len(w.acklist.numbers) > 0
}

View file

@ -0,0 +1,305 @@
// +build !confonly
package kcp
import (
"encoding/binary"
"github.com/xtls/xray-core/v1/common/buf"
)
// Command is a KCP command that indicate the purpose of a Segment.
type Command byte
const (
// CommandACK indicates an AckSegment.
CommandACK Command = 0
// CommandData indicates a DataSegment.
CommandData Command = 1
// CommandTerminate indicates that peer terminates the connection.
CommandTerminate Command = 2
// CommandPing indicates a ping.
CommandPing Command = 3
)
type SegmentOption byte
const (
SegmentOptionClose SegmentOption = 1
)
type Segment interface {
Release()
Conversation() uint16
Command() Command
ByteSize() int32
Serialize([]byte)
parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte)
}
const (
DataSegmentOverhead = 18
)
type DataSegment struct {
Conv uint16
Option SegmentOption
Timestamp uint32
Number uint32
SendingNext uint32
payload *buf.Buffer
timeout uint32
transmit uint32
}
func NewDataSegment() *DataSegment {
return new(DataSegment)
}
func (s *DataSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
s.Conv = conv
s.Option = opt
if len(buf) < 15 {
return false, nil
}
s.Timestamp = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.Number = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.SendingNext = binary.BigEndian.Uint32(buf)
buf = buf[4:]
dataLen := int(binary.BigEndian.Uint16(buf))
buf = buf[2:]
if len(buf) < dataLen {
return false, nil
}
s.Data().Clear()
s.Data().Write(buf[:dataLen])
buf = buf[dataLen:]
return true, buf
}
func (s *DataSegment) Conversation() uint16 {
return s.Conv
}
func (*DataSegment) Command() Command {
return CommandData
}
func (s *DataSegment) Detach() *buf.Buffer {
r := s.payload
s.payload = nil
return r
}
func (s *DataSegment) Data() *buf.Buffer {
if s.payload == nil {
s.payload = buf.New()
}
return s.payload
}
func (s *DataSegment) Serialize(b []byte) {
binary.BigEndian.PutUint16(b, s.Conv)
b[2] = byte(CommandData)
b[3] = byte(s.Option)
binary.BigEndian.PutUint32(b[4:], s.Timestamp)
binary.BigEndian.PutUint32(b[8:], s.Number)
binary.BigEndian.PutUint32(b[12:], s.SendingNext)
binary.BigEndian.PutUint16(b[16:], uint16(s.payload.Len()))
copy(b[18:], s.payload.Bytes())
}
func (s *DataSegment) ByteSize() int32 {
return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len()
}
func (s *DataSegment) Release() {
s.payload.Release()
s.payload = nil
}
type AckSegment struct {
Conv uint16
Option SegmentOption
ReceivingWindow uint32
ReceivingNext uint32
Timestamp uint32
NumberList []uint32
}
const ackNumberLimit = 128
func NewAckSegment() *AckSegment {
return new(AckSegment)
}
func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
s.Conv = conv
s.Option = opt
if len(buf) < 13 {
return false, nil
}
s.ReceivingWindow = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.ReceivingNext = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.Timestamp = binary.BigEndian.Uint32(buf)
buf = buf[4:]
count := int(buf[0])
buf = buf[1:]
if len(buf) < count*4 {
return false, nil
}
for i := 0; i < count; i++ {
s.PutNumber(binary.BigEndian.Uint32(buf))
buf = buf[4:]
}
return true, buf
}
func (s *AckSegment) Conversation() uint16 {
return s.Conv
}
func (*AckSegment) Command() Command {
return CommandACK
}
func (s *AckSegment) PutTimestamp(timestamp uint32) {
if timestamp-s.Timestamp < 0x7FFFFFFF {
s.Timestamp = timestamp
}
}
func (s *AckSegment) PutNumber(number uint32) {
s.NumberList = append(s.NumberList, number)
}
func (s *AckSegment) IsFull() bool {
return len(s.NumberList) == ackNumberLimit
}
func (s *AckSegment) IsEmpty() bool {
return len(s.NumberList) == 0
}
func (s *AckSegment) ByteSize() int32 {
return 2 + 1 + 1 + 4 + 4 + 4 + 1 + int32(len(s.NumberList)*4)
}
func (s *AckSegment) Serialize(b []byte) {
binary.BigEndian.PutUint16(b, s.Conv)
b[2] = byte(CommandACK)
b[3] = byte(s.Option)
binary.BigEndian.PutUint32(b[4:], s.ReceivingWindow)
binary.BigEndian.PutUint32(b[8:], s.ReceivingNext)
binary.BigEndian.PutUint32(b[12:], s.Timestamp)
b[16] = byte(len(s.NumberList))
n := 17
for _, number := range s.NumberList {
binary.BigEndian.PutUint32(b[n:], number)
n += 4
}
}
func (s *AckSegment) Release() {}
type CmdOnlySegment struct {
Conv uint16
Cmd Command
Option SegmentOption
SendingNext uint32
ReceivingNext uint32
PeerRTO uint32
}
func NewCmdOnlySegment() *CmdOnlySegment {
return new(CmdOnlySegment)
}
func (s *CmdOnlySegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
s.Conv = conv
s.Cmd = cmd
s.Option = opt
if len(buf) < 12 {
return false, nil
}
s.SendingNext = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.ReceivingNext = binary.BigEndian.Uint32(buf)
buf = buf[4:]
s.PeerRTO = binary.BigEndian.Uint32(buf)
buf = buf[4:]
return true, buf
}
func (s *CmdOnlySegment) Conversation() uint16 {
return s.Conv
}
func (s *CmdOnlySegment) Command() Command {
return s.Cmd
}
func (*CmdOnlySegment) ByteSize() int32 {
return 2 + 1 + 1 + 4 + 4 + 4
}
func (s *CmdOnlySegment) Serialize(b []byte) {
binary.BigEndian.PutUint16(b, s.Conv)
b[2] = byte(s.Cmd)
b[3] = byte(s.Option)
binary.BigEndian.PutUint32(b[4:], s.SendingNext)
binary.BigEndian.PutUint32(b[8:], s.ReceivingNext)
binary.BigEndian.PutUint32(b[12:], s.PeerRTO)
}
func (*CmdOnlySegment) Release() {}
func ReadSegment(buf []byte) (Segment, []byte) {
if len(buf) < 4 {
return nil, nil
}
conv := binary.BigEndian.Uint16(buf)
buf = buf[2:]
cmd := Command(buf[0])
opt := SegmentOption(buf[1])
buf = buf[2:]
var seg Segment
switch cmd {
case CommandData:
seg = NewDataSegment()
case CommandACK:
seg = NewAckSegment()
default:
seg = NewCmdOnlySegment()
}
valid, extra := seg.parse(conv, cmd, opt, buf)
if !valid {
return nil, nil
}
return seg, extra
}

View file

@ -0,0 +1,107 @@
package kcp_test
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
. "github.com/xtls/xray-core/v1/transport/internet/kcp"
)
func TestBadSegment(t *testing.T) {
seg, buf := ReadSegment(nil)
if seg != nil {
t.Error("non-nil seg")
}
if len(buf) != 0 {
t.Error("buf len: ", len(buf))
}
}
func TestDataSegment(t *testing.T) {
seg := &DataSegment{
Conv: 1,
Timestamp: 3,
Number: 4,
SendingNext: 5,
}
seg.Data().Write([]byte{'a', 'b', 'c', 'd'})
nBytes := seg.ByteSize()
bytes := make([]byte, nBytes)
seg.Serialize(bytes)
iseg, _ := ReadSegment(bytes)
seg2 := iseg.(*DataSegment)
if r := cmp.Diff(seg2, seg, cmpopts.IgnoreUnexported(DataSegment{})); r != "" {
t.Error(r)
}
if r := cmp.Diff(seg2.Data().Bytes(), seg.Data().Bytes()); r != "" {
t.Error(r)
}
}
func Test1ByteDataSegment(t *testing.T) {
seg := &DataSegment{
Conv: 1,
Timestamp: 3,
Number: 4,
SendingNext: 5,
}
seg.Data().WriteByte('a')
nBytes := seg.ByteSize()
bytes := make([]byte, nBytes)
seg.Serialize(bytes)
iseg, _ := ReadSegment(bytes)
seg2 := iseg.(*DataSegment)
if r := cmp.Diff(seg2, seg, cmpopts.IgnoreUnexported(DataSegment{})); r != "" {
t.Error(r)
}
if r := cmp.Diff(seg2.Data().Bytes(), seg.Data().Bytes()); r != "" {
t.Error(r)
}
}
func TestACKSegment(t *testing.T) {
seg := &AckSegment{
Conv: 1,
ReceivingWindow: 2,
ReceivingNext: 3,
Timestamp: 10,
NumberList: []uint32{1, 3, 5, 7, 9},
}
nBytes := seg.ByteSize()
bytes := make([]byte, nBytes)
seg.Serialize(bytes)
iseg, _ := ReadSegment(bytes)
seg2 := iseg.(*AckSegment)
if r := cmp.Diff(seg2, seg); r != "" {
t.Error(r)
}
}
func TestCmdSegment(t *testing.T) {
seg := &CmdOnlySegment{
Conv: 1,
Cmd: CommandPing,
Option: SegmentOptionClose,
SendingNext: 11,
ReceivingNext: 13,
PeerRTO: 15,
}
nBytes := seg.ByteSize()
bytes := make([]byte, nBytes)
seg.Serialize(bytes)
iseg, _ := ReadSegment(bytes)
seg2 := iseg.(*CmdOnlySegment)
if r := cmp.Diff(seg2, seg); r != "" {
t.Error(r)
}
}

View file

@ -0,0 +1,366 @@
// +build !confonly
package kcp
import (
"container/list"
"sync"
"github.com/xtls/xray-core/v1/common/buf"
)
type SendingWindow struct {
cache *list.List
totalInFlightSize uint32
writer SegmentWriter
onPacketLoss func(uint32)
}
func NewSendingWindow(writer SegmentWriter, onPacketLoss func(uint32)) *SendingWindow {
window := &SendingWindow{
cache: list.New(),
writer: writer,
onPacketLoss: onPacketLoss,
}
return window
}
func (sw *SendingWindow) Release() {
if sw == nil {
return
}
for sw.cache.Len() > 0 {
seg := sw.cache.Front().Value.(*DataSegment)
seg.Release()
sw.cache.Remove(sw.cache.Front())
}
}
func (sw *SendingWindow) Len() uint32 {
return uint32(sw.cache.Len())
}
func (sw *SendingWindow) IsEmpty() bool {
return sw.cache.Len() == 0
}
func (sw *SendingWindow) Push(number uint32, b *buf.Buffer) {
seg := NewDataSegment()
seg.Number = number
seg.payload = b
sw.cache.PushBack(seg)
}
func (sw *SendingWindow) FirstNumber() uint32 {
return sw.cache.Front().Value.(*DataSegment).Number
}
func (sw *SendingWindow) Clear(una uint32) {
for !sw.IsEmpty() {
seg := sw.cache.Front().Value.(*DataSegment)
if seg.Number >= una {
break
}
seg.Release()
sw.cache.Remove(sw.cache.Front())
}
}
func (sw *SendingWindow) HandleFastAck(number uint32, rto uint32) {
if sw.IsEmpty() {
return
}
sw.Visit(func(seg *DataSegment) bool {
if number == seg.Number || number-seg.Number > 0x7FFFFFFF {
return false
}
if seg.transmit > 0 && seg.timeout > rto/3 {
seg.timeout -= rto / 3
}
return true
})
}
func (sw *SendingWindow) Visit(visitor func(seg *DataSegment) bool) {
if sw.IsEmpty() {
return
}
for e := sw.cache.Front(); e != nil; e = e.Next() {
seg := e.Value.(*DataSegment)
if !visitor(seg) {
break
}
}
}
func (sw *SendingWindow) Flush(current uint32, rto uint32, maxInFlightSize uint32) {
if sw.IsEmpty() {
return
}
var lost uint32
var inFlightSize uint32
sw.Visit(func(segment *DataSegment) bool {
if current-segment.timeout >= 0x7FFFFFFF {
return true
}
if segment.transmit == 0 {
// First time
sw.totalInFlightSize++
} else {
lost++
}
segment.timeout = current + rto
segment.Timestamp = current
segment.transmit++
sw.writer.Write(segment)
inFlightSize++
return inFlightSize < maxInFlightSize
})
if sw.onPacketLoss != nil && inFlightSize > 0 && sw.totalInFlightSize != 0 {
rate := lost * 100 / sw.totalInFlightSize
sw.onPacketLoss(rate)
}
}
func (sw *SendingWindow) Remove(number uint32) bool {
if sw.IsEmpty() {
return false
}
for e := sw.cache.Front(); e != nil; e = e.Next() {
seg := e.Value.(*DataSegment)
if seg.Number > number {
return false
} else if seg.Number == number {
if sw.totalInFlightSize > 0 {
sw.totalInFlightSize--
}
seg.Release()
sw.cache.Remove(e)
return true
}
}
return false
}
type SendingWorker struct {
sync.RWMutex
conn *Connection
window *SendingWindow
firstUnacknowledged uint32
nextNumber uint32
remoteNextNumber uint32
controlWindow uint32
fastResend uint32
windowSize uint32
firstUnacknowledgedUpdated bool
closed bool
}
func NewSendingWorker(kcp *Connection) *SendingWorker {
worker := &SendingWorker{
conn: kcp,
fastResend: 2,
remoteNextNumber: 32,
controlWindow: kcp.Config.GetSendingInFlightSize(),
windowSize: kcp.Config.GetSendingBufferSize(),
}
worker.window = NewSendingWindow(worker, worker.OnPacketLoss)
return worker
}
func (w *SendingWorker) Release() {
w.Lock()
w.window.Release()
w.closed = true
w.Unlock()
}
func (w *SendingWorker) ProcessReceivingNext(nextNumber uint32) {
w.Lock()
defer w.Unlock()
w.ProcessReceivingNextWithoutLock(nextNumber)
}
func (w *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) {
w.window.Clear(nextNumber)
w.FindFirstUnacknowledged()
}
func (w *SendingWorker) FindFirstUnacknowledged() {
first := w.firstUnacknowledged
if !w.window.IsEmpty() {
w.firstUnacknowledged = w.window.FirstNumber()
} else {
w.firstUnacknowledged = w.nextNumber
}
if first != w.firstUnacknowledged {
w.firstUnacknowledgedUpdated = true
}
}
func (w *SendingWorker) processAck(number uint32) bool {
// number < v.firstUnacknowledged || number >= v.nextNumber
if number-w.firstUnacknowledged > 0x7FFFFFFF || number-w.nextNumber < 0x7FFFFFFF {
return false
}
removed := w.window.Remove(number)
if removed {
w.FindFirstUnacknowledged()
}
return removed
}
func (w *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint32) {
defer seg.Release()
w.Lock()
defer w.Unlock()
if w.closed {
return
}
if w.remoteNextNumber < seg.ReceivingWindow {
w.remoteNextNumber = seg.ReceivingWindow
}
w.ProcessReceivingNextWithoutLock(seg.ReceivingNext)
if seg.IsEmpty() {
return
}
var maxack uint32
var maxackRemoved bool
for _, number := range seg.NumberList {
removed := w.processAck(number)
if maxack < number {
maxack = number
maxackRemoved = removed
}
}
if maxackRemoved {
w.window.HandleFastAck(maxack, rto)
if current-seg.Timestamp < 10000 {
w.conn.roundTrip.Update(current-seg.Timestamp, current)
}
}
}
func (w *SendingWorker) Push(b *buf.Buffer) bool {
w.Lock()
defer w.Unlock()
if w.closed {
return false
}
if w.window.Len() > w.windowSize {
return false
}
w.window.Push(w.nextNumber, b)
w.nextNumber++
return true
}
func (w *SendingWorker) Write(seg Segment) error {
dataSeg := seg.(*DataSegment)
dataSeg.Conv = w.conn.meta.Conversation
dataSeg.SendingNext = w.firstUnacknowledged
dataSeg.Option = 0
if w.conn.State() == StateReadyToClose {
dataSeg.Option = SegmentOptionClose
}
return w.conn.output.Write(dataSeg)
}
func (w *SendingWorker) OnPacketLoss(lossRate uint32) {
if !w.conn.Config.Congestion || w.conn.roundTrip.Timeout() == 0 {
return
}
if lossRate >= 15 {
w.controlWindow = 3 * w.controlWindow / 4
} else if lossRate <= 5 {
w.controlWindow += w.controlWindow / 4
}
if w.controlWindow < 16 {
w.controlWindow = 16
}
if w.controlWindow > 2*w.conn.Config.GetSendingInFlightSize() {
w.controlWindow = 2 * w.conn.Config.GetSendingInFlightSize()
}
}
func (w *SendingWorker) Flush(current uint32) {
w.Lock()
if w.closed {
w.Unlock()
return
}
cwnd := w.conn.Config.GetSendingInFlightSize()
if cwnd > w.remoteNextNumber-w.firstUnacknowledged {
cwnd = w.remoteNextNumber - w.firstUnacknowledged
}
if w.conn.Config.Congestion && cwnd > w.controlWindow {
cwnd = w.controlWindow
}
cwnd *= 20 // magic
if !w.window.IsEmpty() {
w.window.Flush(current, w.conn.roundTrip.Timeout(), cwnd)
w.firstUnacknowledgedUpdated = false
}
updated := w.firstUnacknowledgedUpdated
w.firstUnacknowledgedUpdated = false
w.Unlock()
if updated {
w.conn.Ping(current, CommandPing)
}
}
func (w *SendingWorker) CloseWrite() {
w.Lock()
defer w.Unlock()
w.window.Clear(0xFFFFFFFF)
}
func (w *SendingWorker) IsEmpty() bool {
w.RLock()
defer w.RUnlock()
return w.window.IsEmpty()
}
func (w *SendingWorker) UpdateNecessary() bool {
return !w.IsEmpty()
}
func (w *SendingWorker) FirstUnacknowledged() uint32 {
w.RLock()
defer w.RUnlock()
return w.firstUnacknowledged
}

View file

@ -0,0 +1,17 @@
// +build !amd64
package kcp
// xorfwd performs XOR forwards in words, x[i] ^= x[i-4], i from 0 to len
func xorfwd(x []byte) {
for i := 4; i < len(x); i++ {
x[i] ^= x[i-4]
}
}
// xorbkd performs XOR backwords in words, x[i] ^= x[i-4], i from len to 0
func xorbkd(x []byte) {
for i := len(x) - 1; i >= 4; i-- {
x[i] ^= x[i-4]
}
}

View file

@ -0,0 +1,7 @@
package kcp
//go:noescape
func xorfwd(x []byte)
//go:noescape
func xorbkd(x []byte)

View file

@ -0,0 +1,47 @@
#include "textflag.h"
// func xorfwd(x []byte)
TEXT ·xorfwd(SB),NOSPLIT,$0
MOVQ x+0(FP), SI // x[i]
MOVQ x_len+8(FP), CX // x.len
MOVQ x+0(FP), DI
ADDQ $4, DI // x[i+4]
SUBQ $4, CX
xorfwdloop:
MOVL (SI), AX
XORL AX, (DI)
ADDQ $4, SI
ADDQ $4, DI
SUBQ $4, CX
CMPL CX, $0
JE xorfwddone
JMP xorfwdloop
xorfwddone:
RET
// func xorbkd(x []byte)
TEXT ·xorbkd(SB),NOSPLIT,$0
MOVQ x+0(FP), SI
MOVQ x_len+8(FP), CX // x.len
MOVQ x+0(FP), DI
ADDQ CX, SI // x[-8]
SUBQ $8, SI
ADDQ CX, DI // x[-4]
SUBQ $4, DI
SUBQ $4, CX
xorbkdloop:
MOVL (SI), AX
XORL AX, (DI)
SUBQ $4, SI
SUBQ $4, DI
SUBQ $4, CX
CMPL CX, $0
JE xorbkddone
JMP xorbkdloop
xorbkddone:
RET

View file

@ -0,0 +1,38 @@
package internet
// MemoryStreamConfig is a parsed form of StreamConfig. This is used to reduce number of Protobuf parsing.
type MemoryStreamConfig struct {
ProtocolName string
ProtocolSettings interface{}
SecurityType string
SecuritySettings interface{}
SocketSettings *SocketConfig
}
// ToMemoryStreamConfig converts a StreamConfig to MemoryStreamConfig. It returns a default non-nil MemoryStreamConfig for nil input.
func ToMemoryStreamConfig(s *StreamConfig) (*MemoryStreamConfig, error) {
ets, err := s.GetEffectiveTransportSettings()
if err != nil {
return nil, err
}
mss := &MemoryStreamConfig{
ProtocolName: s.GetEffectiveProtocol(),
ProtocolSettings: ets,
}
if s != nil {
mss.SocketSettings = s.SocketSettings
}
if s != nil && s.HasSecuritySettings() {
ess, err := s.GetEffectiveSecuritySettings()
if err != nil {
return nil, err
}
mss.SecurityType = s.SecurityType
mss.SecuritySettings = ess
}
return mss, nil
}

View file

@ -0,0 +1,49 @@
// +build !confonly
package quic
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/protocol"
"github.com/xtls/xray-core/v1/transport/internet"
"golang.org/x/crypto/chacha20poly1305"
)
func getAuth(config *Config) (cipher.AEAD, error) {
security := config.Security.GetSecurityType()
if security == protocol.SecurityType_NONE {
return nil, nil
}
salted := []byte(config.Key + "xray-quic-salt")
key := sha256.Sum256(salted)
if security == protocol.SecurityType_AES128_GCM {
block, err := aes.NewCipher(key[:16])
common.Must(err)
return cipher.NewGCM(block)
}
if security == protocol.SecurityType_CHACHA20_POLY1305 {
return chacha20poly1305.New(key[:])
}
return nil, newError("unsupported security type")
}
func getHeader(config *Config) (internet.PacketHeader, error) {
if config.Header == nil {
return nil, nil
}
msg, err := config.Header.GetInstance()
if err != nil {
return nil, err
}
return internet.CreatePacketHeader(msg)
}

View file

@ -0,0 +1,190 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: transport/internet/quic/config.proto
package quic
import (
proto "github.com/golang/protobuf/proto"
protocol "github.com/xtls/xray-core/v1/common/protocol"
serial "github.com/xtls/xray-core/v1/common/serial"
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)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Security *protocol.SecurityConfig `protobuf:"bytes,2,opt,name=security,proto3" json:"security,omitempty"`
Header *serial.TypedMessage `protobuf:"bytes,3,opt,name=header,proto3" json:"header,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_transport_internet_quic_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_quic_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_quic_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *Config) GetSecurity() *protocol.SecurityConfig {
if x != nil {
return x.Security
}
return nil
}
func (x *Config) GetHeader() *serial.TypedMessage {
if x != nil {
return x.Header
}
return nil
}
var File_transport_internet_quic_config_proto protoreflect.FileDescriptor
var file_transport_internet_quic_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, 0x71, 0x75, 0x69, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x71, 0x75, 0x69, 0x63, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72,
0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63,
0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x63,
0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42,
0x79, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x71,
0x75, 0x69, 0x63, 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75, 0x69, 0x63, 0xaa, 0x02, 0x1c, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_transport_internet_quic_config_proto_rawDescOnce sync.Once
file_transport_internet_quic_config_proto_rawDescData = file_transport_internet_quic_config_proto_rawDesc
)
func file_transport_internet_quic_config_proto_rawDescGZIP() []byte {
file_transport_internet_quic_config_proto_rawDescOnce.Do(func() {
file_transport_internet_quic_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_quic_config_proto_rawDescData)
})
return file_transport_internet_quic_config_proto_rawDescData
}
var file_transport_internet_quic_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_transport_internet_quic_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: xray.transport.internet.quic.Config
(*protocol.SecurityConfig)(nil), // 1: xray.common.protocol.SecurityConfig
(*serial.TypedMessage)(nil), // 2: xray.common.serial.TypedMessage
}
var file_transport_internet_quic_config_proto_depIdxs = []int32{
1, // 0: xray.transport.internet.quic.Config.security:type_name -> xray.common.protocol.SecurityConfig
2, // 1: xray.transport.internet.quic.Config.header:type_name -> xray.common.serial.TypedMessage
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_quic_config_proto_init() }
func file_transport_internet_quic_config_proto_init() {
if File_transport_internet_quic_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_transport_internet_quic_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_quic_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_transport_internet_quic_config_proto_goTypes,
DependencyIndexes: file_transport_internet_quic_config_proto_depIdxs,
MessageInfos: file_transport_internet_quic_config_proto_msgTypes,
}.Build()
File_transport_internet_quic_config_proto = out.File
file_transport_internet_quic_config_proto_rawDesc = nil
file_transport_internet_quic_config_proto_goTypes = nil
file_transport_internet_quic_config_proto_depIdxs = nil
}

View file

@ -0,0 +1,16 @@
syntax = "proto3";
package xray.transport.internet.quic;
option csharp_namespace = "Xray.Transport.Internet.Quic";
option go_package = "github.com/xtls/xray-core/v1/transport/internet/quic";
option java_package = "com.xray.transport.internet.quic";
option java_multiple_files = true;
import "common/serial/typed_message.proto";
import "common/protocol/headers.proto";
message Config {
string key = 1;
xray.common.protocol.SecurityConfig security = 2;
xray.common.serial.TypedMessage header = 3;
}

View file

@ -0,0 +1,187 @@
// +build !confonly
package quic
import (
"crypto/cipher"
"crypto/rand"
"errors"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/transport/internet"
)
type sysConn struct {
conn net.PacketConn
header internet.PacketHeader
auth cipher.AEAD
}
func wrapSysConn(rawConn net.PacketConn, config *Config) (*sysConn, error) {
header, err := getHeader(config)
if err != nil {
return nil, err
}
auth, err := getAuth(config)
if err != nil {
return nil, err
}
return &sysConn{
conn: rawConn,
header: header,
auth: auth,
}, nil
}
var errInvalidPacket = errors.New("invalid packet")
func (c *sysConn) readFromInternal(p []byte) (int, net.Addr, error) {
buffer := getBuffer()
defer putBuffer(buffer)
nBytes, addr, err := c.conn.ReadFrom(buffer)
if err != nil {
return 0, nil, err
}
payload := buffer[:nBytes]
if c.header != nil {
if len(payload) <= int(c.header.Size()) {
return 0, nil, errInvalidPacket
}
payload = payload[c.header.Size():]
}
if c.auth == nil {
n := copy(p, payload)
return n, addr, nil
}
if len(payload) <= c.auth.NonceSize() {
return 0, nil, errInvalidPacket
}
nonce := payload[:c.auth.NonceSize()]
payload = payload[c.auth.NonceSize():]
p, err = c.auth.Open(p[:0], nonce, payload, nil)
if err != nil {
return 0, nil, errInvalidPacket
}
return len(p), addr, nil
}
func (c *sysConn) ReadFrom(p []byte) (int, net.Addr, error) {
if c.header == nil && c.auth == nil {
return c.conn.ReadFrom(p)
}
for {
n, addr, err := c.readFromInternal(p)
if err != nil && err != errInvalidPacket {
return 0, nil, err
}
if err == nil {
return n, addr, nil
}
}
}
func (c *sysConn) WriteTo(p []byte, addr net.Addr) (int, error) {
if c.header == nil && c.auth == nil {
return c.conn.WriteTo(p, addr)
}
buffer := getBuffer()
defer putBuffer(buffer)
payload := buffer
n := 0
if c.header != nil {
c.header.Serialize(payload)
n = int(c.header.Size())
}
if c.auth == nil {
nBytes := copy(payload[n:], p)
n += nBytes
} else {
nounce := payload[n : n+c.auth.NonceSize()]
common.Must2(rand.Read(nounce))
n += c.auth.NonceSize()
pp := c.auth.Seal(payload[:n], nounce, p, nil)
n = len(pp)
}
return c.conn.WriteTo(payload[:n], addr)
}
func (c *sysConn) Close() error {
return c.conn.Close()
}
func (c *sysConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *sysConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *sysConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *sysConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
type interConn struct {
stream quic.Stream
local net.Addr
remote net.Addr
}
func (c *interConn) Read(b []byte) (int, error) {
return c.stream.Read(b)
}
func (c *interConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
mb = buf.Compact(mb)
mb, err := buf.WriteMultiBuffer(c, mb)
buf.ReleaseMulti(mb)
return err
}
func (c *interConn) Write(b []byte) (int, error) {
return c.stream.Write(b)
}
func (c *interConn) Close() error {
return c.stream.Close()
}
func (c *interConn) LocalAddr() net.Addr {
return c.local
}
func (c *interConn) RemoteAddr() net.Addr {
return c.remote
}
func (c *interConn) SetDeadline(t time.Time) error {
return c.stream.SetDeadline(t)
}
func (c *interConn) SetReadDeadline(t time.Time) error {
return c.stream.SetReadDeadline(t)
}
func (c *interConn) SetWriteDeadline(t time.Time) error {
return c.stream.SetWriteDeadline(t)
}

View file

@ -0,0 +1,218 @@
// +build !confonly
package quic
import (
"context"
"sync"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/task"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
)
type sessionContext struct {
rawConn *sysConn
session quic.Session
}
var errSessionClosed = newError("session closed")
func (c *sessionContext) openStream(destAddr net.Addr) (*interConn, error) {
if !isActive(c.session) {
return nil, errSessionClosed
}
stream, err := c.session.OpenStream()
if err != nil {
return nil, err
}
conn := &interConn{
stream: stream,
local: c.session.LocalAddr(),
remote: destAddr,
}
return conn, nil
}
type clientSessions struct {
access sync.Mutex
sessions map[net.Destination][]*sessionContext
cleanup *task.Periodic
}
func isActive(s quic.Session) bool {
select {
case <-s.Context().Done():
return false
default:
return true
}
}
func removeInactiveSessions(sessions []*sessionContext) []*sessionContext {
activeSessions := make([]*sessionContext, 0, len(sessions))
for _, s := range sessions {
if isActive(s.session) {
activeSessions = append(activeSessions, s)
continue
}
if err := s.session.CloseWithError(0, ""); err != nil {
newError("failed to close session").Base(err).WriteToLog()
}
if err := s.rawConn.Close(); err != nil {
newError("failed to close raw connection").Base(err).WriteToLog()
}
}
if len(activeSessions) < len(sessions) {
return activeSessions
}
return sessions
}
func openStream(sessions []*sessionContext, destAddr net.Addr) *interConn {
for _, s := range sessions {
if !isActive(s.session) {
continue
}
conn, err := s.openStream(destAddr)
if err != nil {
continue
}
return conn
}
return nil
}
func (s *clientSessions) cleanSessions() error {
s.access.Lock()
defer s.access.Unlock()
if len(s.sessions) == 0 {
return nil
}
newSessionMap := make(map[net.Destination][]*sessionContext)
for dest, sessions := range s.sessions {
sessions = removeInactiveSessions(sessions)
if len(sessions) > 0 {
newSessionMap[dest] = sessions
}
}
s.sessions = newSessionMap
return nil
}
func (s *clientSessions) openConnection(destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (internet.Connection, error) {
s.access.Lock()
defer s.access.Unlock()
if s.sessions == nil {
s.sessions = make(map[net.Destination][]*sessionContext)
}
dest := net.DestinationFromAddr(destAddr)
var sessions []*sessionContext
if s, found := s.sessions[dest]; found {
sessions = s
}
if true {
conn := openStream(sessions, destAddr)
if conn != nil {
return conn, nil
}
}
sessions = removeInactiveSessions(sessions)
rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
IP: []byte{0, 0, 0, 0},
Port: 0,
}, sockopt)
if err != nil {
return nil, err
}
quicConfig := &quic.Config{
ConnectionIDLength: 12,
HandshakeTimeout: time.Second * 8,
MaxIdleTimeout: time.Second * 30,
}
conn, err := wrapSysConn(rawConn, config)
if err != nil {
rawConn.Close()
return nil, err
}
session, err := quic.DialContext(context.Background(), conn, destAddr, "", tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig)
if err != nil {
conn.Close()
return nil, err
}
context := &sessionContext{
session: session,
rawConn: conn,
}
s.sessions[dest] = append(sessions, context)
return context.openStream(destAddr)
}
var client clientSessions
func init() {
client.sessions = make(map[net.Destination][]*sessionContext)
client.cleanup = &task.Periodic{
Interval: time.Minute,
Execute: client.cleanSessions,
}
common.Must(client.cleanup.Start())
}
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
if tlsConfig == nil {
tlsConfig = &tls.Config{
ServerName: internalDomain,
AllowInsecure: true,
}
}
var destAddr *net.UDPAddr
if dest.Address.Family().IsIP() {
destAddr = &net.UDPAddr{
IP: dest.Address.IP(),
Port: int(dest.Port),
}
} else {
addr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
if err != nil {
return nil, err
}
destAddr = addr
}
config := streamSettings.ProtocolSettings.(*Config)
return client.openConnection(destAddr, config, tlsConfig, streamSettings.SocketSettings)
}
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
}

View file

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

View file

@ -0,0 +1,140 @@
// +build !confonly
package quic
import (
"context"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/protocol/tls/cert"
"github.com/xtls/xray-core/v1/common/signal/done"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/tls"
)
// Listener is an internet.Listener that listens for TCP connections.
type Listener struct {
rawConn *sysConn
listener quic.Listener
done *done.Instance
addConn internet.ConnHandler
}
func (l *Listener) acceptStreams(session quic.Session) {
for {
stream, err := session.AcceptStream(context.Background())
if err != nil {
newError("failed to accept stream").Base(err).WriteToLog()
select {
case <-session.Context().Done():
return
case <-l.done.Wait():
if err := session.CloseWithError(0, ""); err != nil {
newError("failed to close session").Base(err).WriteToLog()
}
return
default:
time.Sleep(time.Second)
continue
}
}
conn := &interConn{
stream: stream,
local: session.LocalAddr(),
remote: session.RemoteAddr(),
}
l.addConn(conn)
}
}
func (l *Listener) keepAccepting() {
for {
conn, err := l.listener.Accept(context.Background())
if err != nil {
newError("failed to accept QUIC sessions").Base(err).WriteToLog()
if l.done.Done() {
break
}
time.Sleep(time.Second)
continue
}
go l.acceptStreams(conn)
}
}
// Addr implements internet.Listener.Addr.
func (l *Listener) Addr() net.Addr {
return l.listener.Addr()
}
// Close implements internet.Listener.Close.
func (l *Listener) Close() error {
l.done.Close()
l.listener.Close()
l.rawConn.Close()
return nil
}
// Listen creates a new Listener based on configurations.
func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
if address.Family().IsDomain() {
return nil, newError("domain address is not allows for listening quic")
}
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
if tlsConfig == nil {
tlsConfig = &tls.Config{
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.DNSNames(internalDomain), cert.CommonName(internalDomain)))},
}
}
config := streamSettings.ProtocolSettings.(*Config)
rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
IP: address.IP(),
Port: int(port),
}, streamSettings.SocketSettings)
if err != nil {
return nil, err
}
quicConfig := &quic.Config{
ConnectionIDLength: 12,
HandshakeTimeout: time.Second * 8,
MaxIdleTimeout: time.Second * 45,
MaxIncomingStreams: 32,
MaxIncomingUniStreams: -1,
}
conn, err := wrapSysConn(rawConn, config)
if err != nil {
conn.Close()
return nil, err
}
qListener, err := quic.Listen(conn, tlsConfig.GetTLSConfig(), quicConfig)
if err != nil {
conn.Close()
return nil, err
}
listener := &Listener{
done: done.New(),
rawConn: conn,
listener: qListener,
addConn: handler,
}
go listener.keepAccepting()
return listener, nil
}
func init() {
common.Must(internet.RegisterTransportListener(protocolName, Listen))
}

View file

@ -0,0 +1,23 @@
// +build !confonly
package quic
import (
"sync"
"github.com/xtls/xray-core/v1/common/bytespool"
)
var pool *sync.Pool
func init() {
pool = bytespool.GetPool(2048)
}
func getBuffer() []byte {
return pool.Get().([]byte)
}
func putBuffer(p []byte) {
pool.Put(p)
}

View file

@ -0,0 +1,25 @@
// +build !confonly
package quic
import (
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/transport/internet"
)
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
// Here is some modification needs to be done before update quic vendor.
// * use bytespool in buffer_pool.go
// * set MaxReceivePacketSize to 1452 - 32 (16 bytes auth, 16 bytes head)
//
//
const protocolName = "quic"
const internalDomain = "quic.internal.example.com"
func init() {
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
return new(Config)
}))
}

View file

@ -0,0 +1,223 @@
package quic_test
import (
"context"
"crypto/rand"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/buf"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/protocol"
"github.com/xtls/xray-core/v1/common/protocol/tls/cert"
"github.com/xtls/xray-core/v1/common/serial"
"github.com/xtls/xray-core/v1/testing/servers/udp"
"github.com/xtls/xray-core/v1/transport/internet"
"github.com/xtls/xray-core/v1/transport/internet/headers/wireguard"
"github.com/xtls/xray-core/v1/transport/internet/quic"
"github.com/xtls/xray-core/v1/transport/internet/tls"
)
func TestQuicConnection(t *testing.T) {
port := udp.PickPort()
listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{},
SecurityType: "tls",
SecuritySettings: &tls.Config{
Certificate: []*tls.Certificate{
tls.ParseCertificate(
cert.MustGenerate(nil,
cert.DNSNames("www.example.com"),
),
),
},
},
}, func(conn internet.Connection) {
go func() {
defer conn.Close()
b := buf.New()
defer b.Release()
for {
b.Clear()
if _, err := b.ReadFrom(conn); err != nil {
return
}
common.Must2(conn.Write(b.Bytes()))
}
}()
})
common.Must(err)
defer listener.Close()
time.Sleep(time.Second)
dctx := context.Background()
conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{},
SecurityType: "tls",
SecuritySettings: &tls.Config{
ServerName: "www.example.com",
AllowInsecure: true,
},
})
common.Must(err)
defer conn.Close()
const N = 1024
b1 := make([]byte, N)
common.Must2(rand.Read(b1))
b2 := buf.New()
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
}
func TestQuicConnectionWithoutTLS(t *testing.T) {
port := udp.PickPort()
listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{},
}, func(conn internet.Connection) {
go func() {
defer conn.Close()
b := buf.New()
defer b.Release()
for {
b.Clear()
if _, err := b.ReadFrom(conn); err != nil {
return
}
common.Must2(conn.Write(b.Bytes()))
}
}()
})
common.Must(err)
defer listener.Close()
time.Sleep(time.Second)
dctx := context.Background()
conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{},
})
common.Must(err)
defer conn.Close()
const N = 1024
b1 := make([]byte, N)
common.Must2(rand.Read(b1))
b2 := buf.New()
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
}
func TestQuicConnectionAuthHeader(t *testing.T) {
port := udp.PickPort()
listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{
Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}),
Key: "abcd",
Security: &protocol.SecurityConfig{
Type: protocol.SecurityType_AES128_GCM,
},
},
}, func(conn internet.Connection) {
go func() {
defer conn.Close()
b := buf.New()
defer b.Release()
for {
b.Clear()
if _, err := b.ReadFrom(conn); err != nil {
return
}
common.Must2(conn.Write(b.Bytes()))
}
}()
})
common.Must(err)
defer listener.Close()
time.Sleep(time.Second)
dctx := context.Background()
conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
ProtocolName: "quic",
ProtocolSettings: &quic.Config{
Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}),
Key: "abcd",
Security: &protocol.SecurityConfig{
Type: protocol.SecurityType_AES128_GCM,
},
},
})
common.Must(err)
defer conn.Close()
const N = 1024
b1 := make([]byte, N)
common.Must2(rand.Read(b1))
b2 := buf.New()
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
common.Must2(conn.Write(b1))
b2.Clear()
common.Must2(b2.ReadFullFrom(conn, N))
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
t.Error(r)
}
}

View file

@ -0,0 +1,19 @@
package internet
func isTCPSocket(network string) bool {
switch network {
case "tcp", "tcp4", "tcp6":
return true
default:
return false
}
}
func isUDPSocket(network string) bool {
switch network {
case "udp", "udp4", "udp6":
return true
default:
return false
}
}

View file

@ -0,0 +1,60 @@
package internet
import (
"syscall"
)
const (
// TCP_FASTOPEN is the socket option on darwin for TCP fast open.
TCP_FASTOPEN = 0x105
// TCP_FASTOPEN_SERVER is the value to enable TCP fast open on darwin for server connections.
TCP_FASTOPEN_SERVER = 0x01
// TCP_FASTOPEN_CLIENT is the value to enable TCP fast open on darwin for client connections.
TCP_FASTOPEN_CLIENT = 0x02
)
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, TCP_FASTOPEN_CLIENT); err != nil {
return err
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, 0); err != nil {
return err
}
}
}
return nil
}
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, TCP_FASTOPEN_SERVER); err != nil {
return err
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, 0); err != nil {
return err
}
}
}
return nil
}
func bindAddr(fd uintptr, address []byte, port uint32) error {
return nil
}
func setReuseAddr(fd uintptr) error {
return nil
}
func setReusePort(fd uintptr) error {
return nil
}

View file

@ -0,0 +1,230 @@
package internet
import (
"encoding/binary"
"net"
"os"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
const (
sysPFINOUT = 0x0
sysPFIN = 0x1
sysPFOUT = 0x2
sysPFFWD = 0x3
sysDIOCNATLOOK = 0xc04c4417
)
type pfiocNatlook struct {
Saddr [16]byte /* pf_addr */
Daddr [16]byte /* pf_addr */
Rsaddr [16]byte /* pf_addr */
Rdaddr [16]byte /* pf_addr */
Sport uint16
Dport uint16
Rsport uint16
Rdport uint16
Af uint8
Proto uint8
Direction uint8
Pad [1]byte
}
const (
sizeofPfiocNatlook = 0x4c
soReUsePort = 0x00000200
soReUsePortLB = 0x00010000
)
func ioctl(s uintptr, ioc int, b []byte) error {
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, s, uintptr(ioc), uintptr(unsafe.Pointer(&b[0]))); errno != 0 {
return error(errno)
}
return nil
}
func (nl *pfiocNatlook) rdPort() int {
return int(binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&nl.Rdport))[:]))
}
func (nl *pfiocNatlook) setPort(remote, local int) {
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Sport))[:], uint16(remote))
binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Dport))[:], uint16(local))
}
// OriginalDst uses ioctl to read original destination from /dev/pf
func OriginalDst(la, ra net.Addr) (net.IP, int, error) {
f, err := os.Open("/dev/pf")
if err != nil {
return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err)
}
defer f.Close()
fd := f.Fd()
b := make([]byte, sizeofPfiocNatlook)
nl := (*pfiocNatlook)(unsafe.Pointer(&b[0]))
var raIP, laIP net.IP
var raPort, laPort int
switch la.(type) {
case *net.TCPAddr:
raIP = ra.(*net.TCPAddr).IP
laIP = la.(*net.TCPAddr).IP
raPort = ra.(*net.TCPAddr).Port
laPort = la.(*net.TCPAddr).Port
nl.Proto = syscall.IPPROTO_TCP
case *net.UDPAddr:
raIP = ra.(*net.UDPAddr).IP
laIP = la.(*net.UDPAddr).IP
raPort = ra.(*net.UDPAddr).Port
laPort = la.(*net.UDPAddr).Port
nl.Proto = syscall.IPPROTO_UDP
}
if raIP.To4() != nil {
if laIP.IsUnspecified() {
laIP = net.ParseIP("127.0.0.1")
}
copy(nl.Saddr[:net.IPv4len], raIP.To4())
copy(nl.Daddr[:net.IPv4len], laIP.To4())
nl.Af = syscall.AF_INET
}
if raIP.To16() != nil && raIP.To4() == nil {
if laIP.IsUnspecified() {
laIP = net.ParseIP("::1")
}
copy(nl.Saddr[:], raIP)
copy(nl.Daddr[:], laIP)
nl.Af = syscall.AF_INET6
}
nl.setPort(raPort, laPort)
ioc := uintptr(sysDIOCNATLOOK)
for _, dir := range []byte{sysPFOUT, sysPFIN} {
nl.Direction = dir
err = ioctl(fd, int(ioc), b)
if err == nil || err != syscall.ENOENT {
break
}
}
if err != nil {
return net.IP{}, -1, os.NewSyscallError("ioctl", err)
}
odPort := nl.rdPort()
var odIP net.IP
switch nl.Af {
case syscall.AF_INET:
odIP = make(net.IP, net.IPv4len)
copy(odIP, nl.Rdaddr[:net.IPv4len])
case syscall.AF_INET6:
odIP = make(net.IP, net.IPv6len)
copy(odIP, nl.Rdaddr[:])
}
return odIP, odPort, nil
}
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
if config.Mark != 0 {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
return newError("failed to set SO_USER_COOKIE").Base(err)
}
}
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 1); err != nil {
return newError("failed to set TCP_FASTOPEN_CONNECT=1").Base(err)
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil {
return newError("failed to set TCP_FASTOPEN_CONNECT=0").Base(err)
}
}
}
if config.Tproxy.IsEnabled() {
ip, _, _ := net.SplitHostPort(address)
if net.ParseIP(ip).To4() != nil {
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
return newError("failed to set outbound IP_BINDANY").Base(err)
}
} else {
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
return newError("failed to set outbound IPV6_BINDANY").Base(err)
}
}
}
return nil
}
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if config.Mark != 0 {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
return newError("failed to set SO_USER_COOKIE").Base(err)
}
}
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 1); err != nil {
return newError("failed to set TCP_FASTOPEN=1").Base(err)
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil {
return newError("failed to set TCP_FASTOPEN=0").Base(err)
}
}
}
if config.Tproxy.IsEnabled() {
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
return newError("failed to set inbound IP_BINDANY").Base(err)
}
}
}
return nil
}
func bindAddr(fd uintptr, ip []byte, port uint32) error {
setReuseAddr(fd)
setReusePort(fd)
var sockaddr syscall.Sockaddr
switch len(ip) {
case net.IPv4len:
a4 := &syscall.SockaddrInet4{
Port: int(port),
}
copy(a4.Addr[:], ip)
sockaddr = a4
case net.IPv6len:
a6 := &syscall.SockaddrInet6{
Port: int(port),
}
copy(a6.Addr[:], ip)
sockaddr = a6
default:
return newError("unexpected length of ip")
}
return syscall.Bind(int(fd), sockaddr)
}
func setReuseAddr(fd uintptr) error {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return newError("failed to set SO_REUSEADDR").Base(err).AtWarning()
}
return nil
}
func setReusePort(fd uintptr) error {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil {
return newError("failed to set SO_REUSEPORT").Base(err).AtWarning()
}
}
return nil
}

View file

@ -0,0 +1,120 @@
package internet
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
const (
// For incoming connections.
TCP_FASTOPEN = 23
// For out-going connections.
TCP_FASTOPEN_CONNECT = 30
)
func bindAddr(fd uintptr, ip []byte, port uint32) error {
setReuseAddr(fd)
setReusePort(fd)
var sockaddr syscall.Sockaddr
switch len(ip) {
case net.IPv4len:
a4 := &syscall.SockaddrInet4{
Port: int(port),
}
copy(a4.Addr[:], ip)
sockaddr = a4
case net.IPv6len:
a6 := &syscall.SockaddrInet6{
Port: int(port),
}
copy(a6.Addr[:], ip)
sockaddr = a6
default:
return newError("unexpected length of ip")
}
return syscall.Bind(int(fd), sockaddr)
}
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
if config.Mark != 0 {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil {
return newError("failed to set SO_MARK").Base(err)
}
}
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, 1); err != nil {
return newError("failed to set TCP_FASTOPEN_CONNECT=1").Base(err)
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, 0); err != nil {
return newError("failed to set TCP_FASTOPEN_CONNECT=0").Base(err)
}
}
}
if config.Tproxy.IsEnabled() {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
return newError("failed to set IP_TRANSPARENT").Base(err)
}
}
return nil
}
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if config.Mark != 0 {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil {
return newError("failed to set SO_MARK").Base(err)
}
}
if isTCPSocket(network) {
switch config.Tfo {
case SocketConfig_Enable:
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN, 1); err != nil {
return newError("failed to set TCP_FASTOPEN=1").Base(err)
}
case SocketConfig_Disable:
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN, 0); err != nil {
return newError("failed to set TCP_FASTOPEN=0").Base(err)
}
}
}
if config.Tproxy.IsEnabled() {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
return newError("failed to set IP_TRANSPARENT").Base(err)
}
}
if config.ReceiveOriginalDestAddress && isUDPSocket(network) {
err1 := syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1)
err2 := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)
if err1 != nil && err2 != nil {
return err1
}
}
return nil
}
func setReuseAddr(fd uintptr) error {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return newError("failed to set SO_REUSEADDR").Base(err).AtWarning()
}
return nil
}
func setReusePort(fd uintptr) error {
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
return newError("failed to set SO_REUSEPORT").Base(err).AtWarning()
}
return nil
}

Some files were not shown because too many files have changed in this diff Show more