mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-30 17:38:41 +00:00
Transport: Remove QUIC (#3754)
https://github.com/XTLS/Xray-core/pull/3554#issuecomment-2236171077
This commit is contained in:
parent
ab3c00e96b
commit
9a953c070f
15 changed files with 0 additions and 1348 deletions
|
@ -1,48 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/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, errors.New("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)
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.0
|
||||
// source: transport/internet/quic/config.proto
|
||||
|
||||
package quic
|
||||
|
||||
import (
|
||||
protocol "github.com/xtls/xray-core/common/protocol"
|
||||
serial "github.com/xtls/xray-core/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)
|
||||
)
|
||||
|
||||
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,
|
||||
0x76, 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, 0x31, 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, 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 = []any{
|
||||
(*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 any, i int) any {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_transport_internet_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
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.quic;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Quic";
|
||||
option go_package = "github.com/xtls/xray-core/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;
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type sysConn struct {
|
||||
conn *net.UDPConn
|
||||
header internet.PacketHeader
|
||||
auth cipher.AEAD
|
||||
}
|
||||
|
||||
func wrapSysConn(rawConn *net.UDPConn, 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) SetReadBuffer(bytes int) error {
|
||||
return c.conn.SetReadBuffer(bytes)
|
||||
}
|
||||
|
||||
func (c *sysConn) SetWriteBuffer(bytes int) error {
|
||||
return c.conn.SetWriteBuffer(bytes)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (c *sysConn) SyscallConn() (syscall.RawConn, error) {
|
||||
return c.conn.SyscallConn()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
"github.com/quic-go/quic-go/qlog"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
type connectionContext struct {
|
||||
rawConn *sysConn
|
||||
conn quic.Connection
|
||||
}
|
||||
|
||||
var errConnectionClosed = errors.New("connection closed")
|
||||
|
||||
func (c *connectionContext) openStream(destAddr net.Addr) (*interConn, error) {
|
||||
if !isActive(c.conn) {
|
||||
return nil, errConnectionClosed
|
||||
}
|
||||
|
||||
stream, err := c.conn.OpenStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn := &interConn{
|
||||
stream: stream,
|
||||
local: c.conn.LocalAddr(),
|
||||
remote: destAddr,
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type clientConnections struct {
|
||||
access sync.Mutex
|
||||
conns map[net.Destination][]*connectionContext
|
||||
cleanup *task.Periodic
|
||||
}
|
||||
|
||||
func isActive(s quic.Connection) bool {
|
||||
select {
|
||||
case <-s.Context().Done():
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func removeInactiveConnections(conns []*connectionContext) []*connectionContext {
|
||||
activeConnections := make([]*connectionContext, 0, len(conns))
|
||||
for i, s := range conns {
|
||||
if isActive(s.conn) {
|
||||
activeConnections = append(activeConnections, s)
|
||||
continue
|
||||
}
|
||||
|
||||
errors.LogInfo(context.Background(), "closing quic connection at index: ", i)
|
||||
if err := s.conn.CloseWithError(0, ""); err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to close connection")
|
||||
}
|
||||
if err := s.rawConn.Close(); err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to close raw connection")
|
||||
}
|
||||
}
|
||||
|
||||
if len(activeConnections) < len(conns) {
|
||||
errors.LogInfo(context.Background(), "active quic connection reduced from ", len(conns), " to ", len(activeConnections))
|
||||
return activeConnections
|
||||
}
|
||||
|
||||
return conns
|
||||
}
|
||||
|
||||
func (s *clientConnections) cleanConnections() error {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
|
||||
if len(s.conns) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
newConnMap := make(map[net.Destination][]*connectionContext)
|
||||
|
||||
for dest, conns := range s.conns {
|
||||
conns = removeInactiveConnections(conns)
|
||||
if len(conns) > 0 {
|
||||
newConnMap[dest] = conns
|
||||
}
|
||||
}
|
||||
|
||||
s.conns = newConnMap
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *clientConnections) openConnection(ctx context.Context, destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (stat.Connection, error) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
|
||||
if s.conns == nil {
|
||||
s.conns = make(map[net.Destination][]*connectionContext)
|
||||
}
|
||||
|
||||
dest := net.DestinationFromAddr(destAddr)
|
||||
|
||||
var conns []*connectionContext
|
||||
if s, found := s.conns[dest]; found {
|
||||
conns = s
|
||||
}
|
||||
|
||||
if len(conns) > 0 {
|
||||
s := conns[len(conns)-1]
|
||||
if isActive(s.conn) {
|
||||
conn, err := s.openStream(destAddr)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
errors.LogInfoInner(ctx, err, "failed to openStream: ")
|
||||
} else {
|
||||
errors.LogInfo(ctx, "current quic connection is not active!")
|
||||
}
|
||||
}
|
||||
|
||||
conns = removeInactiveConnections(conns)
|
||||
errors.LogInfo(ctx, "dialing quic to ", dest)
|
||||
rawConn, err := internet.DialSystem(ctx, dest, sockopt)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
|
||||
}
|
||||
|
||||
quicConfig := &quic.Config{
|
||||
KeepAlivePeriod: 0,
|
||||
HandshakeIdleTimeout: time.Second * 8,
|
||||
MaxIdleTimeout: time.Second * 300,
|
||||
Tracer: func(ctx context.Context, p logging.Perspective, ci quic.ConnectionID) *logging.ConnectionTracer {
|
||||
return qlog.NewConnectionTracer(&QlogWriter{connID: ci}, p, ci)
|
||||
},
|
||||
}
|
||||
|
||||
var udpConn *net.UDPConn
|
||||
switch conn := rawConn.(type) {
|
||||
case *net.UDPConn:
|
||||
udpConn = conn
|
||||
case *internet.PacketConnWrapper:
|
||||
udpConn = conn.Conn.(*net.UDPConn)
|
||||
default:
|
||||
// TODO: Support sockopt for QUIC
|
||||
rawConn.Close()
|
||||
return nil, errors.New("QUIC with sockopt is unsupported").AtWarning()
|
||||
}
|
||||
|
||||
sysConn, err := wrapSysConn(udpConn, config)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
tr := quic.Transport{
|
||||
ConnectionIDLength: 12,
|
||||
Conn: sysConn,
|
||||
}
|
||||
conn, err := tr.Dial(context.Background(), destAddr, tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig)
|
||||
if err != nil {
|
||||
sysConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
context := &connectionContext{
|
||||
conn: conn,
|
||||
rawConn: sysConn,
|
||||
}
|
||||
s.conns[dest] = append(conns, context)
|
||||
return context.openStream(destAddr)
|
||||
}
|
||||
|
||||
var client clientConnections
|
||||
|
||||
func init() {
|
||||
client.conns = make(map[net.Destination][]*connectionContext)
|
||||
client.cleanup = &task.Periodic{
|
||||
Interval: time.Minute,
|
||||
Execute: client.cleanConnections,
|
||||
}
|
||||
common.Must(client.cleanup.Start())
|
||||
}
|
||||
|
||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.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 {
|
||||
dialerIp := internet.DestIpAddress()
|
||||
if dialerIp != nil {
|
||||
destAddr = &net.UDPAddr{
|
||||
IP: dialerIp,
|
||||
Port: int(dest.Port),
|
||||
}
|
||||
errors.LogInfo(ctx, "quic Dial use dialer dest addr: ", destAddr)
|
||||
} else {
|
||||
addr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destAddr = addr
|
||||
}
|
||||
}
|
||||
|
||||
config := streamSettings.ProtocolSettings.(*Config)
|
||||
|
||||
return client.openConnection(ctx, destAddr, config, tlsConfig, streamSettings.SocketSettings)
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
"github.com/quic-go/quic-go/qlog"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/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(conn quic.Connection) {
|
||||
for {
|
||||
stream, err := conn.AcceptStream(context.Background())
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to accept stream")
|
||||
select {
|
||||
case <-conn.Context().Done():
|
||||
return
|
||||
case <-l.done.Wait():
|
||||
if err := conn.CloseWithError(0, ""); err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to close connection")
|
||||
}
|
||||
return
|
||||
default:
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
conn := &interConn{
|
||||
stream: stream,
|
||||
local: conn.LocalAddr(),
|
||||
remote: conn.RemoteAddr(),
|
||||
}
|
||||
|
||||
l.addConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) keepAccepting() {
|
||||
for {
|
||||
conn, err := l.listener.Accept(context.Background())
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to accept QUIC connection")
|
||||
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, errors.New("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{
|
||||
KeepAlivePeriod: 0,
|
||||
HandshakeIdleTimeout: time.Second * 8,
|
||||
MaxIdleTimeout: time.Second * 300,
|
||||
MaxIncomingStreams: 32,
|
||||
MaxIncomingUniStreams: -1,
|
||||
Tracer: func(ctx context.Context, p logging.Perspective, ci quic.ConnectionID) *logging.ConnectionTracer {
|
||||
return qlog.NewConnectionTracer(&QlogWriter{connID: ci}, p, ci)
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := wrapSysConn(rawConn.(*net.UDPConn), config)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
tr := quic.Transport{
|
||||
ConnectionIDLength: 12,
|
||||
Conn: conn,
|
||||
}
|
||||
qListener, err := tr.Listen(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))
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/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)
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package quic
|
||||
|
||||
import "github.com/quic-go/quic-go"
|
||||
|
||||
type QlogWriter struct {
|
||||
connID quic.ConnectionID
|
||||
}
|
||||
|
||||
func (w *QlogWriter) Write(b []byte) (int, error) {
|
||||
// to much log, only turn on when debug Quic
|
||||
|
||||
// if len(b) > 1 { // skip line separator "0a" in qlog
|
||||
// log.Record(&log.GeneralMessage{
|
||||
// Severity: log.Severity_Debug,
|
||||
// Content: fmt.Sprintf("[%x] %s", w.connID, b),
|
||||
// })
|
||||
// }
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (w *QlogWriter) Close() error {
|
||||
// Noop
|
||||
return nil
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/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"
|
||||
internalDomain = "quic.internal.example.com"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||
return new(Config)
|
||||
}))
|
||||
}
|
|
@ -1,223 +0,0 @@
|
|||
package quic_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/testing/servers/udp"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
||||
"github.com/xtls/xray-core/transport/internet/quic"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/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 stat.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 stat.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 stat.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)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue