mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-30 17:38:41 +00:00
v1.0.0
This commit is contained in:
parent
47d23e9972
commit
c7f7c08ead
711 changed files with 82154 additions and 2 deletions
124
transport/internet/config.go
Normal file
124
transport/internet/config.go
Normal 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
|
||||
}
|
711
transport/internet/config.pb.go
Normal file
711
transport/internet/config.pb.go
Normal 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
|
||||
}
|
90
transport/internet/config.proto
Normal file
90
transport/internet/config.proto
Normal 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;
|
||||
}
|
34
transport/internet/connection.go
Normal file
34
transport/internet/connection.go
Normal 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
|
||||
}
|
72
transport/internet/dialer.go
Normal file
72
transport/internet/dialer.go
Normal 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)
|
||||
}
|
27
transport/internet/dialer_test.go
Normal file
27
transport/internet/dialer_test.go
Normal 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()
|
||||
}
|
38
transport/internet/domainsocket/config.go
Normal file
38
transport/internet/domainsocket/config.go
Normal 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)
|
||||
}))
|
||||
}
|
185
transport/internet/domainsocket/config.pb.go
Normal file
185
transport/internet/domainsocket/config.pb.go
Normal 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
|
||||
}
|
20
transport/internet/domainsocket/config.proto
Normal file
20
transport/internet/domainsocket/config.proto
Normal 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;
|
||||
}
|
40
transport/internet/domainsocket/dial.go
Normal file
40
transport/internet/domainsocket/dial.go
Normal 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))
|
||||
}
|
3
transport/internet/domainsocket/errgen.go
Normal file
3
transport/internet/domainsocket/errgen.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package domainsocket
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
9
transport/internet/domainsocket/errors.generated.go
Normal file
9
transport/internet/domainsocket/errors.generated.go
Normal 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{})
|
||||
}
|
138
transport/internet/domainsocket/listener.go
Normal file
138
transport/internet/domainsocket/listener.go
Normal 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))
|
||||
}
|
92
transport/internet/domainsocket/listener_test.go
Normal file
92
transport/internet/domainsocket/listener_test.go
Normal 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())
|
||||
}
|
||||
}
|
9
transport/internet/errors.generated.go
Normal file
9
transport/internet/errors.generated.go
Normal 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{})
|
||||
}
|
11
transport/internet/filelocker.go
Normal file
11
transport/internet/filelocker.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package internet
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// FileLocker is UDS access lock
|
||||
type FileLocker struct {
|
||||
path string
|
||||
file *os.File
|
||||
}
|
36
transport/internet/filelocker_other.go
Normal file
36
transport/internet/filelocker_other.go
Normal 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()
|
||||
}
|
||||
}
|
11
transport/internet/filelocker_windows.go
Normal file
11
transport/internet/filelocker_windows.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package internet
|
||||
|
||||
// Acquire lock
|
||||
func (fl *FileLocker) Acquire() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release lock
|
||||
func (fl *FileLocker) Release() {
|
||||
return
|
||||
}
|
40
transport/internet/header.go
Normal file
40
transport/internet/header.go
Normal 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")
|
||||
}
|
49
transport/internet/header_test.go
Normal file
49
transport/internet/header_test.go
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
100
transport/internet/headers/http/config.go
Normal file
100
transport/internet/headers/http/config.go
Normal 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
|
||||
}
|
654
transport/internet/headers/http/config.pb.go
Normal file
654
transport/internet/headers/http/config.pb.go
Normal 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
|
||||
}
|
65
transport/internet/headers/http/config.proto
Normal file
65
transport/internet/headers/http/config.proto
Normal 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;
|
||||
}
|
9
transport/internet/headers/http/errors.generated.go
Normal file
9
transport/internet/headers/http/errors.generated.go
Normal 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{})
|
||||
}
|
321
transport/internet/headers/http/http.go
Normal file
321
transport/internet/headers/http/http.go
Normal 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))
|
||||
}))
|
||||
}
|
305
transport/internet/headers/http/http_test.go
Normal file
305
transport/internet/headers/http/http_test.go
Normal 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))
|
||||
}
|
||||
}
|
11
transport/internet/headers/http/linkedreadRequest.go
Normal file
11
transport/internet/headers/http/linkedreadRequest.go
Normal 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)
|
49
transport/internet/headers/http/resp.go
Normal file
49
transport/internet/headers/http/resp.go
Normal 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"},
|
||||
},
|
||||
},
|
||||
}
|
200
transport/internet/headers/noop/config.pb.go
Normal file
200
transport/internet/headers/noop/config.pb.go
Normal 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
|
||||
}
|
11
transport/internet/headers/noop/config.proto
Normal file
11
transport/internet/headers/noop/config.proto
Normal 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 {}
|
40
transport/internet/headers/noop/noop.go
Normal file
40
transport/internet/headers/noop/noop.go
Normal 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))
|
||||
}
|
208
transport/internet/headers/srtp/config.pb.go
Normal file
208
transport/internet/headers/srtp/config.pb.go
Normal 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
|
||||
}
|
16
transport/internet/headers/srtp/config.proto
Normal file
16
transport/internet/headers/srtp/config.proto
Normal 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;
|
||||
}
|
37
transport/internet/headers/srtp/srtp.go
Normal file
37
transport/internet/headers/srtp/srtp.go
Normal 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))
|
||||
}
|
27
transport/internet/headers/srtp/srtp_test.go
Normal file
27
transport/internet/headers/srtp/srtp_test.go
Normal 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())
|
||||
}
|
||||
}
|
148
transport/internet/headers/tls/config.pb.go
Normal file
148
transport/internet/headers/tls/config.pb.go
Normal 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
|
||||
}
|
9
transport/internet/headers/tls/config.proto
Normal file
9
transport/internet/headers/tls/config.proto
Normal 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 {}
|
55
transport/internet/headers/tls/dtls.go
Normal file
55
transport/internet/headers/tls/dtls.go
Normal 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))
|
||||
}
|
26
transport/internet/headers/tls/dtls_test.go
Normal file
26
transport/internet/headers/tls/dtls_test.go
Normal 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())
|
||||
}
|
||||
}
|
158
transport/internet/headers/utp/config.pb.go
Normal file
158
transport/internet/headers/utp/config.pb.go
Normal 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
|
||||
}
|
11
transport/internet/headers/utp/config.proto
Normal file
11
transport/internet/headers/utp/config.proto
Normal 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;
|
||||
}
|
39
transport/internet/headers/utp/utp.go
Normal file
39
transport/internet/headers/utp/utp.go
Normal 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))
|
||||
}
|
26
transport/internet/headers/utp/utp_test.go
Normal file
26
transport/internet/headers/utp/utp_test.go
Normal 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())
|
||||
}
|
||||
}
|
149
transport/internet/headers/wechat/config.pb.go
Normal file
149
transport/internet/headers/wechat/config.pb.go
Normal 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
|
||||
}
|
9
transport/internet/headers/wechat/config.proto
Normal file
9
transport/internet/headers/wechat/config.proto
Normal 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 {}
|
43
transport/internet/headers/wechat/wechat.go
Normal file
43
transport/internet/headers/wechat/wechat.go
Normal 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))
|
||||
}
|
24
transport/internet/headers/wechat/wechat_test.go
Normal file
24
transport/internet/headers/wechat/wechat_test.go
Normal 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())
|
||||
}
|
||||
}
|
150
transport/internet/headers/wireguard/config.pb.go
Normal file
150
transport/internet/headers/wireguard/config.pb.go
Normal 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
|
||||
}
|
9
transport/internet/headers/wireguard/config.proto
Normal file
9
transport/internet/headers/wireguard/config.proto
Normal 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 {}
|
30
transport/internet/headers/wireguard/wireguard.go
Normal file
30
transport/internet/headers/wireguard/wireguard.go
Normal 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))
|
||||
}
|
49
transport/internet/http/config.go
Normal file
49
transport/internet/http/config.go
Normal 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)
|
||||
}))
|
||||
}
|
165
transport/internet/http/config.pb.go
Normal file
165
transport/internet/http/config.pb.go
Normal 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
|
||||
}
|
12
transport/internet/http/config.proto
Normal file
12
transport/internet/http/config.proto
Normal 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;
|
||||
}
|
138
transport/internet/http/dialer.go
Normal file
138
transport/internet/http/dialer.go
Normal 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))
|
||||
}
|
9
transport/internet/http/errors.generated.go
Normal file
9
transport/internet/http/errors.generated.go
Normal 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{})
|
||||
}
|
3
transport/internet/http/http.go
Normal file
3
transport/internet/http/http.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package http
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
94
transport/internet/http/http_test.go
Normal file
94
transport/internet/http/http_test.go
Normal 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)
|
||||
}
|
||||
}
|
205
transport/internet/http/hub.go
Normal file
205
transport/internet/http/hub.go
Normal 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))
|
||||
}
|
3
transport/internet/internet.go
Normal file
3
transport/internet/internet.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package internet
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
110
transport/internet/kcp/config.go
Normal file
110
transport/internet/kcp/config.go
Normal 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)
|
||||
}))
|
||||
}
|
774
transport/internet/kcp/config.pb.go
Normal file
774
transport/internet/kcp/config.pb.go
Normal 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
|
||||
}
|
61
transport/internet/kcp/config.proto
Normal file
61
transport/internet/kcp/config.proto
Normal 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;
|
||||
}
|
663
transport/internet/kcp/connection.go
Normal file
663
transport/internet/kcp/connection.go
Normal 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()
|
||||
}
|
38
transport/internet/kcp/connection_test.go
Normal file
38
transport/internet/kcp/connection_test.go
Normal 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))
|
||||
}
|
78
transport/internet/kcp/crypt.go
Normal file
78
transport/internet/kcp/crypt.go
Normal 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
|
||||
}
|
38
transport/internet/kcp/crypt_test.go
Normal file
38
transport/internet/kcp/crypt_test.go
Normal 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)
|
||||
}
|
||||
}
|
15
transport/internet/kcp/cryptreal.go
Normal file
15
transport/internet/kcp/cryptreal.go
Normal 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)
|
||||
}
|
102
transport/internet/kcp/dialer.go
Normal file
102
transport/internet/kcp/dialer.go
Normal 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))
|
||||
}
|
9
transport/internet/kcp/errors.generated.go
Normal file
9
transport/internet/kcp/errors.generated.go
Normal 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{})
|
||||
}
|
97
transport/internet/kcp/io.go
Normal file
97
transport/internet/kcp/io.go
Normal 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
|
||||
}
|
36
transport/internet/kcp/io_test.go
Normal file
36
transport/internet/kcp/io_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
8
transport/internet/kcp/kcp.go
Normal file
8
transport/internet/kcp/kcp.go
Normal 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
|
85
transport/internet/kcp/kcp_test.go
Normal file
85
transport/internet/kcp/kcp_test.go
Normal 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)
|
||||
}
|
||||
}
|
206
transport/internet/kcp/listener.go
Normal file
206
transport/internet/kcp/listener.go
Normal 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))
|
||||
}
|
56
transport/internet/kcp/output.go
Normal file
56
transport/internet/kcp/output.go
Normal 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)
|
||||
})
|
||||
}
|
260
transport/internet/kcp/receiving.go
Normal file
260
transport/internet/kcp/receiving.go
Normal 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
|
||||
}
|
305
transport/internet/kcp/segment.go
Normal file
305
transport/internet/kcp/segment.go
Normal 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
|
||||
}
|
107
transport/internet/kcp/segment_test.go
Normal file
107
transport/internet/kcp/segment_test.go
Normal 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)
|
||||
}
|
||||
}
|
366
transport/internet/kcp/sending.go
Normal file
366
transport/internet/kcp/sending.go
Normal 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
|
||||
}
|
17
transport/internet/kcp/xor.go
Normal file
17
transport/internet/kcp/xor.go
Normal 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]
|
||||
}
|
||||
}
|
7
transport/internet/kcp/xor_amd64.go
Normal file
7
transport/internet/kcp/xor_amd64.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package kcp
|
||||
|
||||
//go:noescape
|
||||
func xorfwd(x []byte)
|
||||
|
||||
//go:noescape
|
||||
func xorbkd(x []byte)
|
47
transport/internet/kcp/xor_amd64.s
Normal file
47
transport/internet/kcp/xor_amd64.s
Normal 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
|
38
transport/internet/memory_settings.go
Normal file
38
transport/internet/memory_settings.go
Normal 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
|
||||
}
|
49
transport/internet/quic/config.go
Normal file
49
transport/internet/quic/config.go
Normal 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)
|
||||
}
|
190
transport/internet/quic/config.pb.go
Normal file
190
transport/internet/quic/config.pb.go
Normal 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
|
||||
}
|
16
transport/internet/quic/config.proto
Normal file
16
transport/internet/quic/config.proto
Normal 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;
|
||||
}
|
187
transport/internet/quic/conn.go
Normal file
187
transport/internet/quic/conn.go
Normal 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)
|
||||
}
|
218
transport/internet/quic/dialer.go
Normal file
218
transport/internet/quic/dialer.go
Normal 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))
|
||||
}
|
9
transport/internet/quic/errors.generated.go
Normal file
9
transport/internet/quic/errors.generated.go
Normal 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{})
|
||||
}
|
140
transport/internet/quic/hub.go
Normal file
140
transport/internet/quic/hub.go
Normal 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))
|
||||
}
|
23
transport/internet/quic/pool.go
Normal file
23
transport/internet/quic/pool.go
Normal 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)
|
||||
}
|
25
transport/internet/quic/quic.go
Normal file
25
transport/internet/quic/quic.go
Normal 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)
|
||||
}))
|
||||
}
|
223
transport/internet/quic/quic_test.go
Normal file
223
transport/internet/quic/quic_test.go
Normal 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)
|
||||
}
|
||||
}
|
19
transport/internet/sockopt.go
Normal file
19
transport/internet/sockopt.go
Normal 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
|
||||
}
|
||||
}
|
60
transport/internet/sockopt_darwin.go
Normal file
60
transport/internet/sockopt_darwin.go
Normal 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
|
||||
}
|
230
transport/internet/sockopt_freebsd.go
Normal file
230
transport/internet/sockopt_freebsd.go
Normal 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
|
||||
}
|
120
transport/internet/sockopt_linux.go
Normal file
120
transport/internet/sockopt_linux.go
Normal 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
|
||||
}
|
42
transport/internet/sockopt_linux_test.go
Normal file
42
transport/internet/sockopt_linux_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package internet_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"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 TestSockOptMark(t *testing.T) {
|
||||
t.Skip("requires CAP_NET_ADMIN")
|
||||
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: func(b []byte) []byte {
|
||||
return b
|
||||
},
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
const mark = 1
|
||||
dialer := DefaultSystemDialer{}
|
||||
conn, err := dialer.Dial(context.Background(), nil, dest, &SocketConfig{Mark: mark})
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
rawConn, err := conn.(*net.TCPConn).SyscallConn()
|
||||
common.Must(err)
|
||||
err = rawConn.Control(func(fd uintptr) {
|
||||
m, err := syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK)
|
||||
common.Must(err)
|
||||
if mark != m {
|
||||
t.Fatal("unexpected connection mark", m, " want ", mark)
|
||||
}
|
||||
})
|
||||
common.Must(err)
|
||||
}
|
23
transport/internet/sockopt_other.go
Normal file
23
transport/internet/sockopt_other.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
// +build js dragonfly netbsd openbsd solaris
|
||||
|
||||
package internet
|
||||
|
||||
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func bindAddr(fd uintptr, ip []byte, port uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setReuseAddr(fd uintptr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setReusePort(fd uintptr) error {
|
||||
return nil
|
||||
}
|
40
transport/internet/sockopt_test.go
Normal file
40
transport/internet/sockopt_test.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
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/buf"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/tcp"
|
||||
. "github.com/xtls/xray-core/v1/transport/internet"
|
||||
)
|
||||
|
||||
func TestTCPFastOpen(t *testing.T) {
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: func(b []byte) []byte {
|
||||
return b
|
||||
},
|
||||
}
|
||||
dest, err := tcpServer.StartContext(context.Background(), &SocketConfig{Tfo: SocketConfig_Enable})
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
dialer := DefaultSystemDialer{}
|
||||
conn, err := dialer.Dial(ctx, nil, dest, &SocketConfig{
|
||||
Tfo: SocketConfig_Enable,
|
||||
})
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Write([]byte("abcd"))
|
||||
common.Must(err)
|
||||
|
||||
b := buf.New()
|
||||
common.Must2(b.ReadFrom(conn))
|
||||
if r := cmp.Diff(b.Bytes(), []byte("abcd")); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue