diff --git a/infra/conf/transport_authenticators.go b/infra/conf/transport_authenticators.go index 4004b26b8651..2274c5addc01 100644 --- a/infra/conf/transport_authenticators.go +++ b/infra/conf/transport_authenticators.go @@ -4,72 +4,17 @@ import ( "sort" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/headers/dns" "github.com/xtls/xray-core/transport/internet/headers/http" "github.com/xtls/xray-core/transport/internet/headers/noop" - "github.com/xtls/xray-core/transport/internet/headers/srtp" - "github.com/xtls/xray-core/transport/internet/headers/tls" - "github.com/xtls/xray-core/transport/internet/headers/utp" - "github.com/xtls/xray-core/transport/internet/headers/wechat" - "github.com/xtls/xray-core/transport/internet/headers/wireguard" "google.golang.org/protobuf/proto" ) -type NoOpAuthenticator struct{} - -func (NoOpAuthenticator) Build() (proto.Message, error) { - return new(noop.Config), nil -} - type NoOpConnectionAuthenticator struct{} func (NoOpConnectionAuthenticator) Build() (proto.Message, error) { return new(noop.ConnectionConfig), nil } -type SRTPAuthenticator struct{} - -func (SRTPAuthenticator) Build() (proto.Message, error) { - return new(srtp.Config), nil -} - -type UTPAuthenticator struct{} - -func (UTPAuthenticator) Build() (proto.Message, error) { - return new(utp.Config), nil -} - -type WechatVideoAuthenticator struct{} - -func (WechatVideoAuthenticator) Build() (proto.Message, error) { - return new(wechat.VideoConfig), nil -} - -type WireguardAuthenticator struct{} - -func (WireguardAuthenticator) Build() (proto.Message, error) { - return new(wireguard.WireguardConfig), nil -} - -type DNSAuthenticator struct { - Domain string `json:"domain"` -} - -func (v *DNSAuthenticator) Build() (proto.Message, error) { - config := new(dns.Config) - config.Domain = "www.baidu.com" - if len(v.Domain) > 0 { - config.Domain = v.Domain - } - return config, nil -} - -type DTLSAuthenticator struct{} - -func (DTLSAuthenticator) Build() (proto.Message, error) { - return new(tls.PacketConfig), nil -} - type AuthenticatorRequest struct { Version string `json:"version"` Method string `json:"method"` diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index a451a3102871..8d35ce9e3517 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -16,7 +16,16 @@ import ( "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask/header/dns" + "github.com/xtls/xray-core/transport/internet/finalmask/header/dtls" + "github.com/xtls/xray-core/transport/internet/finalmask/header/srtp" + "github.com/xtls/xray-core/transport/internet/finalmask/header/utp" + "github.com/xtls/xray-core/transport/internet/finalmask/header/wechat" + "github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard" + "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm" + "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original" "github.com/xtls/xray-core/transport/internet/finalmask/salamander" + "github.com/xtls/xray-core/transport/internet/finalmask/xdns" "github.com/xtls/xray-core/transport/internet/httpupgrade" "github.com/xtls/xray-core/transport/internet/hysteria" "github.com/xtls/xray-core/transport/internet/kcp" @@ -29,16 +38,6 @@ import ( ) var ( - kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{ - "none": func() interface{} { return new(NoOpAuthenticator) }, - "srtp": func() interface{} { return new(SRTPAuthenticator) }, - "utp": func() interface{} { return new(UTPAuthenticator) }, - "wechat-video": func() interface{} { return new(WechatVideoAuthenticator) }, - "dtls": func() interface{} { return new(DTLSAuthenticator) }, - "wireguard": func() interface{} { return new(WireguardAuthenticator) }, - "dns": func() interface{} { return new(DNSAuthenticator) }, - }, "type", "") - tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{ "none": func() interface{} { return new(NoOpConnectionAuthenticator) }, "http": func() interface{} { return new(Authenticator) }, @@ -63,9 +62,9 @@ func (c *KCPConfig) Build() (proto.Message, error) { if c.Mtu != nil { mtu := *c.Mtu - if mtu < 576 || mtu > 1460 { - return nil, errors.New("invalid mKCP MTU size: ", mtu).AtError() - } + // if mtu < 576 || mtu > 1460 { + // return nil, errors.New("invalid mKCP MTU size: ", mtu).AtError() + // } config.Mtu = &kcp.MTU{Value: mtu} } if c.Tti != nil { @@ -100,20 +99,8 @@ func (c *KCPConfig) Build() (proto.Message, error) { config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024} } } - if len(c.HeaderConfig) > 0 { - headerConfig, _, err := kcpHeaderLoader.Load(c.HeaderConfig) - if err != nil { - return nil, errors.New("invalid mKCP header config.").Base(err).AtError() - } - ts, err := headerConfig.(Buildable).Build() - if err != nil { - return nil, errors.New("invalid mKCP header config").Base(err).AtError() - } - config.HeaderConfig = serial.ToTypedMessage(ts) - } - - if c.Seed != nil { - config.Seed = &kcp.EncryptionSeed{Seed: *c.Seed} + if c.HeaderConfig != nil || c.Seed != nil { + return nil, errors.PrintRemovedFeatureError("mkcp header & seed", "finalmask/udp header-* & mkcp-original & mkcp-aes128gcm") } return config, nil @@ -1111,10 +1098,80 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { var ( udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ - "salamander": func() interface{} { return new(Salamander) }, + "header-dns": func() interface{} { return new(Dns) }, + "header-dtls": func() interface{} { return new(Dtls) }, + "header-srtp": func() interface{} { return new(Srtp) }, + "header-utp": func() interface{} { return new(Utp) }, + "header-wechat": func() interface{} { return new(Wechat) }, + "header-wireguard": func() interface{} { return new(Wireguard) }, + "mkcp-original": func() interface{} { return new(Original) }, + "mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) }, + "salamander": func() interface{} { return new(Salamander) }, + "xdns": func() interface{} { return new(Xdns) }, }, "type", "settings") ) +type Dns struct { + Domain string `json:"domain"` +} + +func (c *Dns) Build() (proto.Message, error) { + config := &dns.Config{} + config.Domain = "www.baidu.com" + + if len(c.Domain) > 0 { + config.Domain = c.Domain + } + + return config, nil +} + +type Dtls struct{} + +func (c *Dtls) Build() (proto.Message, error) { + return &dtls.Config{}, nil +} + +type Srtp struct{} + +func (c *Srtp) Build() (proto.Message, error) { + return &srtp.Config{}, nil +} + +type Utp struct{} + +func (c *Utp) Build() (proto.Message, error) { + return &utp.Config{}, nil +} + +type Wechat struct{} + +func (c *Wechat) Build() (proto.Message, error) { + return &wechat.Config{}, nil +} + +type Wireguard struct{} + +func (c *Wireguard) Build() (proto.Message, error) { + return &wireguard.Config{}, nil +} + +type Original struct{} + +func (c *Original) Build() (proto.Message, error) { + return &original.Config{}, nil +} + +type Aes128Gcm struct { + Password string `json:"password"` +} + +func (c *Aes128Gcm) Build() (proto.Message, error) { + return &aes128gcm.Config{ + Password: c.Password, + }, nil +} + type Salamander struct { Password string `json:"password"` } @@ -1125,14 +1182,28 @@ func (c *Salamander) Build() (proto.Message, error) { return config, nil } -type FinalMask struct { +type Xdns struct { + Domain string `json:"domain"` +} + +func (c *Xdns) Build() (proto.Message, error) { + if c.Domain == "" { + return nil, errors.New("empty domain") + } + + return &xdns.Config{ + Domain: c.Domain, + }, nil +} + +type Mask struct { Type string `json:"type"` Settings *json.RawMessage `json:"settings"` } -func (c *FinalMask) Build(tcpmaskLoader bool) (proto.Message, error) { +func (c *Mask) Build(tcp bool) (proto.Message, error) { loader := udpmaskLoader - if tcpmaskLoader { + if tcp { return nil, errors.New("") } @@ -1151,12 +1222,17 @@ func (c *FinalMask) Build(tcpmaskLoader bool) (proto.Message, error) { return ts, nil } +type FinalMask struct { + Tcp []Mask `json:"tcp"` + Udp []Mask `json:"udp"` +} + type StreamConfig struct { Address *Address `json:"address"` Port uint16 `json:"port"` Network *TransportProtocol `json:"network"` Security string `json:"security"` - Udpmasks []*FinalMask `json:"udpmasks"` + FinalMask *FinalMask `json:"finalmask"` TLSSettings *TLSConfig `json:"tlsSettings"` REALITYSettings *REALITYConfig `json:"realitySettings"` RAWSettings *TCPConfig `json:"rawSettings"` @@ -1306,12 +1382,21 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { config.SocketSettings = ss } - for _, mask := range c.Udpmasks { - u, err := mask.Build(false) - if err != nil { - return nil, errors.New("failed to build mask with type ", mask.Type).Base(err) + if c.FinalMask != nil { + for _, mask := range c.FinalMask.Tcp { + u, err := mask.Build(true) + if err != nil { + return nil, errors.New("failed to build mask with type ", mask.Type).Base(err) + } + config.Tcpmasks = append(config.Tcpmasks, serial.ToTypedMessage(u)) + } + for _, mask := range c.FinalMask.Udp { + u, err := mask.Build(false) + if err != nil { + return nil, errors.New("failed to build mask with type ", mask.Type).Base(err) + } + config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u)) } - config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u)) } return config, nil diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 198abb3fd2c2..11b58d9215d3 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -63,11 +63,6 @@ import ( // Transport headers _ "github.com/xtls/xray-core/transport/internet/headers/http" _ "github.com/xtls/xray-core/transport/internet/headers/noop" - _ "github.com/xtls/xray-core/transport/internet/headers/srtp" - _ "github.com/xtls/xray-core/transport/internet/headers/tls" - _ "github.com/xtls/xray-core/transport/internet/headers/utp" - _ "github.com/xtls/xray-core/transport/internet/headers/wechat" - _ "github.com/xtls/xray-core/transport/internet/headers/wireguard" // JSON & TOML & YAML _ "github.com/xtls/xray-core/main/json" diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 7ce4d4f329a3..d8a289a780e7 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -4,17 +4,15 @@ import ( "net" ) +type ConnSize interface { + Size() int32 +} + type Udpmask interface { UDP() - WrapConnClient(net.Conn) (net.Conn, error) - WrapConnServer(net.Conn) (net.Conn, error) - - WrapPacketConnClient(net.PacketConn) (net.PacketConn, error) - WrapPacketConnServer(net.PacketConn) (net.PacketConn, error) - - Size() int - Serialize([]byte) + WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) + WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) } type UdpmaskManager struct { @@ -27,66 +25,32 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager { } } -func (m *UdpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) { - var err error - for _, mask := range m.udpmasks { - raw, err = mask.WrapConnClient(raw) - if err != nil { - return nil, err - } - } - return raw, nil -} - -func (m *UdpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) { - var err error - for _, mask := range m.udpmasks { - raw, err = mask.WrapConnServer(raw) - if err != nil { - return nil, err - } - } - return raw, nil -} - func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) { + leaveSize := int32(0) var err error - for _, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnClient(raw) + for i, mask := range m.udpmasks { + raw, err = mask.WrapPacketConnClient(raw, i == len(m.udpmasks)-1, leaveSize, i == 0) if err != nil { return nil, err } + leaveSize += raw.(ConnSize).Size() } return raw, nil } func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) { + leaveSize := int32(0) var err error - for _, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnServer(raw) + for i, mask := range m.udpmasks { + raw, err = mask.WrapPacketConnServer(raw, i == len(m.udpmasks)-1, leaveSize, i == 0) if err != nil { return nil, err } + leaveSize += raw.(ConnSize).Size() } return raw, nil } -func (m *UdpmaskManager) Size() int { - size := 0 - for _, mask := range m.udpmasks { - size += mask.Size() - } - return size -} - -func (m *UdpmaskManager) Serialize(b []byte) { - index := 0 - for _, mask := range m.udpmasks { - mask.Serialize(b[index:]) - index += mask.Size() - } -} - type Tcpmask interface { TCP() diff --git a/transport/internet/finalmask/header/dns/config.go b/transport/internet/finalmask/header/dns/config.go new file mode 100644 index 000000000000..d5aa5cc399e0 --- /dev/null +++ b/transport/internet/finalmask/header/dns/config.go @@ -0,0 +1,16 @@ +package dns + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/dns/config.pb.go b/transport/internet/finalmask/header/dns/config.pb.go new file mode 100644 index 000000000000..33f84036b30c --- /dev/null +++ b/transport/internet/finalmask/header/dns/config.pb.go @@ -0,0 +1,123 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/dns/config.proto + +package dns + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_dns_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_finalmask_header_dns_config_proto_msgTypes[0] + if 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_finalmask_header_dns_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +var File_transport_internet_finalmask_header_dns_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_dns_config_proto_rawDesc = "" + + "\n" + + "4transport/internet/finalmask/header/dns/config.proto\x12,xray.transport.internet.finalmask.header.dns\" \n" + + "\x06Config\x12\x16\n" + + "\x06domain\x18\x01 \x01(\tR\x06domainB\xa6\x01\n" + + "0com.xray.transport.internet.finalmask.header.dnsP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/header/dns\xaa\x02,Xray.Transport.Internet.Finalmask.Header.Dnsb\x06proto3" + +var ( + file_transport_internet_finalmask_header_dns_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_dns_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_dns_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_dns_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dns_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dns_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_dns_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_dns_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.dns.Config +} +var file_transport_internet_finalmask_header_dns_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_finalmask_header_dns_config_proto_init() } +func file_transport_internet_finalmask_header_dns_config_proto_init() { + if File_transport_internet_finalmask_header_dns_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dns_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dns_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_dns_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_dns_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_dns_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_dns_config_proto = out.File + file_transport_internet_finalmask_header_dns_config_proto_goTypes = nil + file_transport_internet_finalmask_header_dns_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/dns/config.proto b/transport/internet/finalmask/header/dns/config.proto new file mode 100644 index 000000000000..10e1baca27b4 --- /dev/null +++ b/transport/internet/finalmask/header/dns/config.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.dns; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Dns"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/dns"; +option java_package = "com.xray.transport.internet.finalmask.header.dns"; +option java_multiple_files = true; + +message Config { + string domain = 1; +} diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go new file mode 100644 index 000000000000..0baa8fec9972 --- /dev/null +++ b/transport/internet/finalmask/header/dns/conn.go @@ -0,0 +1,241 @@ +package dns + +import ( + "encoding/binary" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/errors" +) + +func packDomainName(s string, msg []byte) (off1 int, err error) { + off := 0 + ls := len(s) + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // Except for escaped dots (\.), which are normal dots. + // There is also a trailing zero. + + // Emit sequence of counted strings, chopping at dots. + var ( + begin int + bs []byte + ) + for i := 0; i < ls; i++ { + var c byte + if bs == nil { + c = s[i] + } else { + c = bs[i] + } + + switch c { + case '\\': + if off+1 > len(msg) { + return len(msg), errors.New("buffer size too small") + } + + if bs == nil { + bs = []byte(s) + } + + copy(bs[i:ls-1], bs[i+1:]) + ls-- + case '.': + labelLen := i - begin + if labelLen >= 1<<6 { // top two bits of length must be clear + return len(msg), errors.New("bad rdata") + } + + // off can already (we're in a loop) be bigger than len(msg) + // this happens when a name isn't fully qualified + if off+1+labelLen > len(msg) { + return len(msg), errors.New("buffer size too small") + } + + // The following is covered by the length check above. + msg[off] = byte(labelLen) + + if bs == nil { + copy(msg[off+1:], s[begin:i]) + } else { + copy(msg[off+1:], bs[begin:i]) + } + off += 1 + labelLen + begin = i + 1 + default: + } + } + + if off < len(msg) { + msg[off] = 0 + } + + return off + 1, nil +} + +type dns struct { + header []byte +} + +func (h *dns) Size() int32 { + return int32(len(h.header)) +} + +func (h *dns) Serialize(b []byte) { + copy(b, h.header) + binary.BigEndian.PutUint16(b[0:], dice.RollUint16()) +} + +type dnsConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *dns + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + var header []byte + header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID + header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query + header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions + header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs + buf := make([]byte, 0x100) + off1, err := packDomainName(c.Domain+".", buf) + if err != nil { + return nil, err + } + header = append(header, buf[:off1]...) + header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A + header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN + + conn := &dnsConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &dns{ + header: header, + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *dnsConn) Size() int32 { + return c.header.Size() +} + +func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *dnsConn) Close() error { + return c.conn.Close() +} + +func (c *dnsConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *dnsConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *dnsConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *dnsConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/header/dtls/config.go b/transport/internet/finalmask/header/dtls/config.go new file mode 100644 index 000000000000..ccce33decfdb --- /dev/null +++ b/transport/internet/finalmask/header/dtls/config.go @@ -0,0 +1,16 @@ +package dtls + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/dtls/config.pb.go b/transport/internet/finalmask/header/dtls/config.pb.go new file mode 100644 index 000000000000..ca8618585af1 --- /dev/null +++ b/transport/internet/finalmask/header/dtls/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/dtls/config.proto + +package dtls + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_dtls_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_dtls_config_proto_msgTypes[0] + if 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_finalmask_header_dtls_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_header_dtls_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_dtls_config_proto_rawDesc = "" + + "\n" + + "5transport/internet/finalmask/header/dtls/config.proto\x12-xray.transport.internet.finalmask.header.dtls\"\b\n" + + "\x06ConfigB\xa9\x01\n" + + "1com.xray.transport.internet.finalmask.header.dtlsP\x01ZBgithub.com/xtls/xray-core/transport/internet/finalmask/header/dtls\xaa\x02-Xray.Transport.Internet.Finalmask.Header.Dtlsb\x06proto3" + +var ( + file_transport_internet_finalmask_header_dtls_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_dtls_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_dtls_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_dtls_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_dtls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_dtls_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_dtls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_dtls_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.dtls.Config +} +var file_transport_internet_finalmask_header_dtls_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_finalmask_header_dtls_config_proto_init() } +func file_transport_internet_finalmask_header_dtls_config_proto_init() { + if File_transport_internet_finalmask_header_dtls_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_dtls_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_dtls_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_dtls_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_dtls_config_proto = out.File + file_transport_internet_finalmask_header_dtls_config_proto_goTypes = nil + file_transport_internet_finalmask_header_dtls_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/dtls/config.proto b/transport/internet/finalmask/header/dtls/config.proto new file mode 100644 index 000000000000..7f9f79718108 --- /dev/null +++ b/transport/internet/finalmask/header/dtls/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.dtls; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Dtls"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/dtls"; +option java_package = "com.xray.transport.internet.finalmask.header.dtls"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go new file mode 100644 index 000000000000..8b12f83e3ea8 --- /dev/null +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -0,0 +1,178 @@ +package dtls + +import ( + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/errors" +) + +type dtls struct { + epoch uint16 + length uint16 + sequence uint32 +} + +func (*dtls) Size() int32 { + return 1 + 2 + 2 + 6 + 2 +} + +func (h *dtls) Serialize(b []byte) { + b[0] = 23 + b[1] = 254 + b[2] = 253 + b[3] = byte(h.epoch >> 8) + b[4] = byte(h.epoch) + b[5] = 0 + b[6] = 0 + b[7] = byte(h.sequence >> 24) + b[8] = byte(h.sequence >> 16) + b[9] = byte(h.sequence >> 8) + b[10] = byte(h.sequence) + h.sequence++ + b[11] = byte(h.length >> 8) + b[12] = byte(h.length) + h.length += 17 + if h.length > 100 { + h.length -= 50 + } +} + +type dtlsConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *dtls + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &dtlsConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &dtls{ + epoch: dice.RollUint16(), + sequence: 0, + length: 17, + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *dtlsConn) Size() int32 { + return c.header.Size() +} + +func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *dtlsConn) Close() error { + return c.conn.Close() +} + +func (c *dtlsConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *dtlsConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *dtlsConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *dtlsConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/header/srtp/config.go b/transport/internet/finalmask/header/srtp/config.go new file mode 100644 index 000000000000..45def61629ca --- /dev/null +++ b/transport/internet/finalmask/header/srtp/config.go @@ -0,0 +1,16 @@ +package srtp + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/srtp/config.pb.go b/transport/internet/finalmask/header/srtp/config.pb.go new file mode 100644 index 000000000000..c3eb54482391 --- /dev/null +++ b/transport/internet/finalmask/header/srtp/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/srtp/config.proto + +package srtp + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_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_finalmask_header_srtp_config_proto_msgTypes[0] + if 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_finalmask_header_srtp_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_header_srtp_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_srtp_config_proto_rawDesc = "" + + "\n" + + "5transport/internet/finalmask/header/srtp/config.proto\x12-xray.transport.internet.finalmask.header.srtp\"\b\n" + + "\x06ConfigB\xa9\x01\n" + + "1com.xray.transport.internet.finalmask.header.srtpP\x01ZBgithub.com/xtls/xray-core/transport/internet/finalmask/header/srtp\xaa\x02-Xray.Transport.Internet.Finalmask.Header.Srtpb\x06proto3" + +var ( + file_transport_internet_finalmask_header_srtp_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_srtp_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_srtp_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_srtp_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_srtp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_srtp_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_srtp_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.srtp.Config +} +var file_transport_internet_finalmask_header_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_finalmask_header_srtp_config_proto_init() } +func file_transport_internet_finalmask_header_srtp_config_proto_init() { + if File_transport_internet_finalmask_header_srtp_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_srtp_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_srtp_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_srtp_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_srtp_config_proto = out.File + file_transport_internet_finalmask_header_srtp_config_proto_goTypes = nil + file_transport_internet_finalmask_header_srtp_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/srtp/config.proto b/transport/internet/finalmask/header/srtp/config.proto new file mode 100644 index 000000000000..6792cb91218b --- /dev/null +++ b/transport/internet/finalmask/header/srtp/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.srtp; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Srtp"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"; +option java_package = "com.xray.transport.internet.finalmask.header.srtp"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go new file mode 100644 index 000000000000..bb03bce70953 --- /dev/null +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -0,0 +1,162 @@ +package srtp + +import ( + "encoding/binary" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/errors" +) + +type srtp struct { + header uint16 + number uint16 +} + +func (*srtp) Size() int32 { + return 4 +} + +func (h *srtp) Serialize(b []byte) { + h.number++ + binary.BigEndian.PutUint16(b, h.header) + binary.BigEndian.PutUint16(b[2:], h.number) +} + +type srtpConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *srtp + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &srtpConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &srtp{ + header: 0xB5E8, + number: dice.RollUint16(), + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *srtpConn) Size() int32 { + return c.header.Size() +} + +func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *srtpConn) Close() error { + return c.conn.Close() +} + +func (c *srtpConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *srtpConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *srtpConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *srtpConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/header/utp/config.go b/transport/internet/finalmask/header/utp/config.go new file mode 100644 index 000000000000..a579d48366e8 --- /dev/null +++ b/transport/internet/finalmask/header/utp/config.go @@ -0,0 +1,16 @@ +package utp + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/utp/config.pb.go b/transport/internet/finalmask/header/utp/config.pb.go new file mode 100644 index 000000000000..5419b25442e1 --- /dev/null +++ b/transport/internet/finalmask/header/utp/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/utp/config.proto + +package utp + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_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_finalmask_header_utp_config_proto_msgTypes[0] + if 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_finalmask_header_utp_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_header_utp_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_utp_config_proto_rawDesc = "" + + "\n" + + "4transport/internet/finalmask/header/utp/config.proto\x12,xray.transport.internet.finalmask.header.utp\"\b\n" + + "\x06ConfigB\xa6\x01\n" + + "0com.xray.transport.internet.finalmask.header.utpP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/header/utp\xaa\x02,Xray.Transport.Internet.Finalmask.Header.Utpb\x06proto3" + +var ( + file_transport_internet_finalmask_header_utp_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_utp_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_utp_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_utp_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_utp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_utp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_utp_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_utp_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_utp_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.utp.Config +} +var file_transport_internet_finalmask_header_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_finalmask_header_utp_config_proto_init() } +func file_transport_internet_finalmask_header_utp_config_proto_init() { + if File_transport_internet_finalmask_header_utp_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_utp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_utp_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_utp_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_utp_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_utp_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_utp_config_proto = out.File + file_transport_internet_finalmask_header_utp_config_proto_goTypes = nil + file_transport_internet_finalmask_header_utp_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/utp/config.proto b/transport/internet/finalmask/header/utp/config.proto new file mode 100644 index 000000000000..ce76ef2ea834 --- /dev/null +++ b/transport/internet/finalmask/header/utp/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.utp; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Utp"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/utp"; +option java_package = "com.xray.transport.internet.finalmask.header.utp"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go new file mode 100644 index 000000000000..5d48193ca74e --- /dev/null +++ b/transport/internet/finalmask/header/utp/conn.go @@ -0,0 +1,164 @@ +package utp + +import ( + "encoding/binary" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/errors" +) + +type utp struct { + header byte + extension byte + connectionID uint16 +} + +func (*utp) Size() int32 { + return 4 +} + +func (h *utp) Serialize(b []byte) { + binary.BigEndian.PutUint16(b, h.connectionID) + b[2] = h.header + b[3] = h.extension +} + +type utpConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *utp + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &utpConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &utp{ + header: 1, + extension: 0, + connectionID: dice.RollUint16(), + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *utpConn) Size() int32 { + return c.header.Size() +} + +func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *utpConn) Close() error { + return c.conn.Close() +} + +func (c *utpConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *utpConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *utpConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *utpConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/header/wechat/config.go b/transport/internet/finalmask/header/wechat/config.go new file mode 100644 index 000000000000..34971ace3a72 --- /dev/null +++ b/transport/internet/finalmask/header/wechat/config.go @@ -0,0 +1,16 @@ +package wechat + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/wechat/config.pb.go b/transport/internet/finalmask/header/wechat/config.pb.go new file mode 100644 index 000000000000..5a9e9f8550af --- /dev/null +++ b/transport/internet/finalmask/header/wechat/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/wechat/config.proto + +package wechat + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_wechat_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_finalmask_header_wechat_config_proto_msgTypes[0] + if 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_finalmask_header_wechat_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_header_wechat_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_wechat_config_proto_rawDesc = "" + + "\n" + + "7transport/internet/finalmask/header/wechat/config.proto\x12/xray.transport.internet.finalmask.header.wechat\"\b\n" + + "\x06ConfigB\xaf\x01\n" + + "3com.xray.transport.internet.finalmask.header.wechatP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/wechat\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Wechatb\x06proto3" + +var ( + file_transport_internet_finalmask_header_wechat_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_wechat_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_wechat_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_wechat_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_wechat_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_wechat_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_wechat_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.wechat.Config +} +var file_transport_internet_finalmask_header_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_finalmask_header_wechat_config_proto_init() } +func file_transport_internet_finalmask_header_wechat_config_proto_init() { + if File_transport_internet_finalmask_header_wechat_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_wechat_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_wechat_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_wechat_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_wechat_config_proto = out.File + file_transport_internet_finalmask_header_wechat_config_proto_goTypes = nil + file_transport_internet_finalmask_header_wechat_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/wechat/config.proto b/transport/internet/finalmask/header/wechat/config.proto new file mode 100644 index 000000000000..3127773a06e6 --- /dev/null +++ b/transport/internet/finalmask/header/wechat/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.wechat; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Wechat"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/wechat"; +option java_package = "com.xray.transport.internet.finalmask.header.wechat"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go new file mode 100644 index 000000000000..f1f56c49d72d --- /dev/null +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -0,0 +1,168 @@ +package wechat + +import ( + "encoding/binary" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/errors" +) + +type wechat struct { + sn uint32 +} + +func (*wechat) Size() int32 { + return 13 +} + +func (h *wechat) Serialize(b []byte) { + h.sn++ + b[0] = 0xa1 + b[1] = 0x08 + binary.BigEndian.PutUint32(b[2:], h.sn) + b[6] = 0x00 + b[7] = 0x10 + b[8] = 0x11 + b[9] = 0x18 + b[10] = 0x30 + b[11] = 0x22 + b[12] = 0x30 +} + +type wechatConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *wechat + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &wechatConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &wechat{ + sn: uint32(dice.RollUint16()), + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *wechatConn) Size() int32 { + return c.header.Size() +} + +func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *wechatConn) Close() error { + return c.conn.Close() +} + +func (c *wechatConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *wechatConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *wechatConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *wechatConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/header/wireguard/config.go b/transport/internet/finalmask/header/wireguard/config.go new file mode 100644 index 000000000000..5eeee34ba6ae --- /dev/null +++ b/transport/internet/finalmask/header/wireguard/config.go @@ -0,0 +1,16 @@ +package wireguard + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/wireguard/config.pb.go b/transport/internet/finalmask/header/wireguard/config.pb.go new file mode 100644 index 000000000000..a42ae16ae959 --- /dev/null +++ b/transport/internet/finalmask/header/wireguard/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/header/wireguard/config.proto + +package wireguard + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_header_wireguard_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_finalmask_header_wireguard_config_proto_msgTypes[0] + if 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_finalmask_header_wireguard_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_header_wireguard_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc = "" + + "\n" + + ":transport/internet/finalmask/header/wireguard/config.proto\x122xray.transport.internet.finalmask.header.wireguard\"\b\n" + + "\x06ConfigB\xb8\x01\n" + + "6com.xray.transport.internet.finalmask.header.wireguardP\x01ZGgithub.com/xtls/xray-core/transport/internet/finalmask/header/wireguard\xaa\x022Xray.Transport.Internet.Finalmask.Header.Wireguardb\x06proto3" + +var ( + file_transport_internet_finalmask_header_wireguard_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_wireguard_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_wireguard_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_header_wireguard_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.header.wireguard.Config +} +var file_transport_internet_finalmask_header_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_finalmask_header_wireguard_config_proto_init() } +func file_transport_internet_finalmask_header_wireguard_config_proto_init() { + if File_transport_internet_finalmask_header_wireguard_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_wireguard_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_wireguard_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_wireguard_config_proto = out.File + file_transport_internet_finalmask_header_wireguard_config_proto_goTypes = nil + file_transport_internet_finalmask_header_wireguard_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/wireguard/config.proto b/transport/internet/finalmask/header/wireguard/config.proto new file mode 100644 index 000000000000..476cbfba1dbe --- /dev/null +++ b/transport/internet/finalmask/header/wireguard/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.wireguard; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Wireguard"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard"; +option java_package = "com.xray.transport.internet.finalmask.header.wireguard"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go new file mode 100644 index 000000000000..f4bf17748242 --- /dev/null +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -0,0 +1,155 @@ +package wireguard + +import ( + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common/errors" +) + +type wireguare struct{} + +func (*wireguare) Size() int32 { + return 4 +} + +func (h *wireguare) Serialize(b []byte) { + b[0] = 0x04 + b[1] = 0x00 + b[2] = 0x00 + b[3] = 0x00 +} + +type wireguareConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + header *wireguare + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &wireguareConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + header: &wireguare{}, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *wireguareConn) Size() int32 { + return c.header.Size() +} + +func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + copy(p, p[c.Size():n]) + + return n - int(c.Size()), addr, err +} + +func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) + + return c.conn.WriteTo(p, addr) +} + +func (c *wireguareConn) Close() error { + return c.conn.Close() +} + +func (c *wireguareConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *wireguareConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *wireguareConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *wireguareConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/mkcp/aes128gcm/aes128gcm_test.go b/transport/internet/finalmask/mkcp/aes128gcm/aes128gcm_test.go new file mode 100644 index 000000000000..4806dfc2c125 --- /dev/null +++ b/transport/internet/finalmask/mkcp/aes128gcm/aes128gcm_test.go @@ -0,0 +1,70 @@ +package aes128gcm_test + +import ( + "crypto/rand" + "crypto/sha256" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/xtls/xray-core/common/crypto" +) + +func TestAes128GcmSealInPlace(t *testing.T) { + hashedPsk := sha256.Sum256([]byte("psk")) + aead := crypto.NewAesGcm(hashedPsk[:16]) + + text := []byte("0123456789012") + buf := make([]byte, 8192) + + nonceSize := aead.NonceSize() + nonce := buf[:nonceSize] + rand.Read(nonce) + copy(buf[nonceSize:], text) + plaintext := buf[nonceSize : nonceSize+len(text)] + + sealed := aead.Seal(nil, nonce, plaintext, nil) + + _ = aead.Seal(plaintext[:0], nonce, plaintext, nil) + + assert.Equal(t, sealed, buf[nonceSize:nonceSize+aead.Overhead()+len(text)]) +} + +func encrypted(plain []byte) ([]byte, []byte) { + hashedPsk := sha256.Sum256([]byte("psk")) + aead := crypto.NewAesGcm(hashedPsk[:16]) + + nonce := make([]byte, 12) + rand.Read(nonce) + + return nonce, aead.Seal(nil, nonce, plain, nil) +} + +func TestAes128GcmOpenInPlace(t *testing.T) { + a, b := encrypted([]byte("0123456789012")) + buf := make([]byte, 8192) + copy(buf, a) + copy(buf[len(a):], b) + + hashedPsk := sha256.Sum256([]byte("psk")) + aead := crypto.NewAesGcm(hashedPsk[:16]) + + nonceSize := aead.NonceSize() + nonce := buf[:nonceSize] + ciphertext := buf[nonceSize : nonceSize+len(b)] + + opened, _ := aead.Open(nil, nonce, ciphertext, nil) + _, _ = aead.Open(ciphertext[:0], nonce, ciphertext, nil) + + assert.Equal(t, opened, ciphertext[:len(ciphertext)-aead.Overhead()]) +} + +func TestAes128GcmBounce(t *testing.T) { + hashedPsk := sha256.Sum256([]byte("psk")) + aead := crypto.NewAesGcm(hashedPsk[:16]) + buf := make([]byte, aead.NonceSize()+aead.Overhead()) + for i := 0; i < 1000; i++ { + _, _ = rand.Read(buf) + _, err := aead.Open(buf[aead.NonceSize():aead.NonceSize()], buf[:aead.NonceSize()], buf[aead.NonceSize():], nil) + assert.NotEqual(t, err, nil) + } +} diff --git a/transport/internet/finalmask/mkcp/aes128gcm/config.go b/transport/internet/finalmask/mkcp/aes128gcm/config.go new file mode 100644 index 000000000000..595dd4ee93cb --- /dev/null +++ b/transport/internet/finalmask/mkcp/aes128gcm/config.go @@ -0,0 +1,16 @@ +package aes128gcm + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/mkcp/aes128gcm/config.pb.go b/transport/internet/finalmask/mkcp/aes128gcm/config.pb.go new file mode 100644 index 000000000000..a403c843092f --- /dev/null +++ b/transport/internet/finalmask/mkcp/aes128gcm/config.pb.go @@ -0,0 +1,123 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/mkcp/aes128gcm/config.proto + +package aes128gcm + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_mkcp_aes128gcm_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_finalmask_mkcp_aes128gcm_config_proto_msgTypes[0] + if 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_finalmask_mkcp_aes128gcm_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +var File_transport_internet_finalmask_mkcp_aes128gcm_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc = "" + + "\n" + + "8transport/internet/finalmask/mkcp/aes128gcm/config.proto\x120xray.transport.internet.finalmask.mkcp.aes128gcm\"$\n" + + "\x06Config\x12\x1a\n" + + "\bpassword\x18\x01 \x01(\tR\bpasswordB\xb2\x01\n" + + "4com.xray.transport.internet.finalmask.mkcp.aes128gcmP\x01ZEgithub.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm\xaa\x020Xray.Transport.Internet.Finalmask.Mkcp.Aes128Gcmb\x06proto3" + +var ( + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData +} + +var file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.mkcp.aes128gcm.Config +} +var file_transport_internet_finalmask_mkcp_aes128gcm_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_finalmask_mkcp_aes128gcm_config_proto_init() } +func file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_init() { + if File_transport_internet_finalmask_mkcp_aes128gcm_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_mkcp_aes128gcm_config_proto = out.File + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes = nil + file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/mkcp/aes128gcm/config.proto b/transport/internet/finalmask/mkcp/aes128gcm/config.proto new file mode 100644 index 000000000000..76af2401beef --- /dev/null +++ b/transport/internet/finalmask/mkcp/aes128gcm/config.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.mkcp.aes128gcm; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Mkcp.Aes128Gcm"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"; +option java_package = "com.xray.transport.internet.finalmask.mkcp.aes128gcm"; +option java_multiple_files = true; + +message Config { + string password = 1; +} diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go new file mode 100644 index 000000000000..9f36fc2adeaf --- /dev/null +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -0,0 +1,174 @@ +package aes128gcm + +import ( + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/crypto" + "github.com/xtls/xray-core/common/errors" +) + +type aes128gcmConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + aead cipher.AEAD + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + hashedPsk := sha256.Sum256([]byte(c.Password)) + + conn := &aes128gcmConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + aead: crypto.NewAesGcm(hashedPsk[:16]), + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *aes128gcmConn) Size() int32 { + return int32(c.aead.NonceSize()) + int32(c.aead.Overhead()) +} + +func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + nonceSize := c.aead.NonceSize() + nonce := c.readBuf[:nonceSize] + ciphertext := c.readBuf[nonceSize:n] + _, err = c.aead.Open(p[:0], nonce, ciphertext, nil) + if err != nil { + c.readMutex.Unlock() + return 0, addr, errors.New("aead open").Base(err) + } + + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + nonceSize := c.aead.NonceSize() + nonce := p[:nonceSize] + ciphertext := p[nonceSize:n] + _, err = c.aead.Open(ciphertext[:0], nonce, ciphertext, nil) + if err != nil { + return 0, addr, errors.New("aead open").Base(err) + } + copy(p, p[nonceSize:n-c.aead.Overhead()]) + + return n - int(c.Size()), addr, nil +} + +func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+int32(c.aead.NonceSize()):], p) + // n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + nonceSize := c.aead.NonceSize() + nonce := c.writeBuf[c.leaveSize : c.leaveSize+int32(nonceSize)] + common.Must2(rand.Read(nonce)) + // copy(c.writeBuf[c.leaveSize+int32(nonceSize):], c.writeBuf[c.leaveSize+c.Size():n]) + plaintext := c.writeBuf[c.leaveSize+int32(nonceSize) : n-c.aead.Overhead()] + _ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + nonceSize := c.aead.NonceSize() + nonce := p[c.leaveSize : c.leaveSize+int32(nonceSize)] + common.Must2(rand.Read(nonce)) + copy(p[c.leaveSize+int32(nonceSize):], p[c.leaveSize+c.Size():]) + plaintext := p[c.leaveSize+int32(nonceSize) : len(p)-c.aead.Overhead()] + _ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil) + + return c.conn.WriteTo(p, addr) +} + +func (c *aes128gcmConn) Close() error { + return c.conn.Close() +} + +func (c *aes128gcmConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *aes128gcmConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *aes128gcmConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *aes128gcmConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/mkcp/original/config.go b/transport/internet/finalmask/mkcp/original/config.go new file mode 100644 index 000000000000..026c979d625a --- /dev/null +++ b/transport/internet/finalmask/mkcp/original/config.go @@ -0,0 +1,16 @@ +package original + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/mkcp/original/config.pb.go b/transport/internet/finalmask/mkcp/original/config.pb.go new file mode 100644 index 000000000000..95a2a763b92b --- /dev/null +++ b/transport/internet/finalmask/mkcp/original/config.pb.go @@ -0,0 +1,114 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/mkcp/original/config.proto + +package original + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_mkcp_original_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_finalmask_mkcp_original_config_proto_msgTypes[0] + if 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_finalmask_mkcp_original_config_proto_rawDescGZIP(), []int{0} +} + +var File_transport_internet_finalmask_mkcp_original_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc = "" + + "\n" + + "7transport/internet/finalmask/mkcp/original/config.proto\x12/xray.transport.internet.finalmask.mkcp.original\"\b\n" + + "\x06ConfigB\xaf\x01\n" + + "3com.xray.transport.internet.finalmask.mkcp.originalP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/mkcp/original\xaa\x02/Xray.Transport.Internet.Finalmask.Mkcp.Originalb\x06proto3" + +var ( + file_transport_internet_finalmask_mkcp_original_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_mkcp_original_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_mkcp_original_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData +} + +var file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_mkcp_original_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.mkcp.original.Config +} +var file_transport_internet_finalmask_mkcp_original_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_finalmask_mkcp_original_config_proto_init() } +func file_transport_internet_finalmask_mkcp_original_config_proto_init() { + if File_transport_internet_finalmask_mkcp_original_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_mkcp_original_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_mkcp_original_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_mkcp_original_config_proto = out.File + file_transport_internet_finalmask_mkcp_original_config_proto_goTypes = nil + file_transport_internet_finalmask_mkcp_original_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/mkcp/original/config.proto b/transport/internet/finalmask/mkcp/original/config.proto new file mode 100644 index 000000000000..4b0f26306dcd --- /dev/null +++ b/transport/internet/finalmask/mkcp/original/config.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.mkcp.original; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Mkcp.Original"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"; +option java_package = "com.xray.transport.internet.finalmask.mkcp.original"; +option java_multiple_files = true; + +message Config {} diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go new file mode 100644 index 000000000000..c97c8d7c8712 --- /dev/null +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -0,0 +1,225 @@ +package original + +import ( + "crypto/cipher" + "encoding/binary" + "hash/fnv" + "io" + "net" + sync "sync" + "time" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/errors" +) + +type simple struct{} + +func NewSimple() *simple { + return &simple{} +} + +func (*simple) NonceSize() int { + return 0 +} + +func (*simple) Overhead() int { + return 6 +} + +func (a *simple) Seal(dst, nonce, plain, extra []byte) []byte { + dst = append(dst, 0, 0, 0, 0, 0, 0) + 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 +} + +func (a *simple) 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, errors.New("invalid auth") + } + + length := binary.BigEndian.Uint16(dst[4:6]) + if len(dst)-6 != int(length) { + return nil, errors.New("invalid auth") + } + + return dst[6:], nil +} + +type simpleConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + aead cipher.AEAD + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &simpleConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + aead: &simple{}, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *simpleConn) Size() int32 { + return int32(c.aead.NonceSize()) + int32(c.aead.Overhead()) +} + +func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + ciphertext := c.readBuf[:n] + opened, err := c.aead.Open(nil, nil, ciphertext, nil) + if err != nil { + c.readMutex.Unlock() + return 0, addr, errors.New("aead open").Base(err) + } + + copy(p, opened) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + } + + ciphertext := p[:n] + opened, err := c.aead.Open(nil, nil, ciphertext, nil) + if err != nil { + c.readMutex.Unlock() + return 0, addr, errors.New("aead open").Base(err) + } + + copy(p, opened) + + return n - int(c.Size()), addr, nil +} + +func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + plaintext := c.writeBuf[c.leaveSize+c.Size() : n] + sealed := c.aead.Seal(nil, nil, plaintext, nil) + copy(c.writeBuf[c.leaveSize:], sealed) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + plaintext := p[c.leaveSize+c.Size():] + sealed := c.aead.Seal(nil, nil, plaintext, nil) + copy(p[c.leaveSize:], sealed) + + return c.conn.WriteTo(p, addr) +} + +func (c *simpleConn) Close() error { + return c.conn.Close() +} + +func (c *simpleConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *simpleConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *simpleConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *simpleConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/mkcp/original/simple_test.go b/transport/internet/finalmask/mkcp/original/simple_test.go new file mode 100644 index 000000000000..f7db54748df8 --- /dev/null +++ b/transport/internet/finalmask/mkcp/original/simple_test.go @@ -0,0 +1,19 @@ +package original_test + +import ( + "crypto/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original" +) + +func TestOriginalBounce(t *testing.T) { + aead := original.NewSimple() + buf := make([]byte, aead.NonceSize()+aead.Overhead()) + for i := 0; i < 1000; i++ { + _, _ = rand.Read(buf) + _, err := aead.Open(buf[:0], nil, buf, nil) + assert.NotEqual(t, err, nil) + } +} diff --git a/transport/internet/kcp/xor.go b/transport/internet/finalmask/mkcp/original/xor.go similarity index 95% rename from transport/internet/kcp/xor.go rename to transport/internet/finalmask/mkcp/original/xor.go index 233a2729ab23..b2a061796741 100644 --- a/transport/internet/kcp/xor.go +++ b/transport/internet/finalmask/mkcp/original/xor.go @@ -1,7 +1,7 @@ //go:build !amd64 // +build !amd64 -package kcp +package original // xorfwd performs XOR forwards in words, x[i] ^= x[i-4], i from 0 to len func xorfwd(x []byte) { diff --git a/transport/internet/kcp/xor_amd64.go b/transport/internet/finalmask/mkcp/original/xor_amd64.go similarity index 81% rename from transport/internet/kcp/xor_amd64.go rename to transport/internet/finalmask/mkcp/original/xor_amd64.go index 94a4dfc8a4a8..7352ace98bb2 100644 --- a/transport/internet/kcp/xor_amd64.go +++ b/transport/internet/finalmask/mkcp/original/xor_amd64.go @@ -1,4 +1,4 @@ -package kcp +package original //go:noescape func xorfwd(x []byte) diff --git a/transport/internet/kcp/xor_amd64.s b/transport/internet/finalmask/mkcp/original/xor_amd64.s similarity index 100% rename from transport/internet/kcp/xor_amd64.s rename to transport/internet/finalmask/mkcp/original/xor_amd64.s diff --git a/transport/internet/finalmask/salamander/config.go b/transport/internet/finalmask/salamander/config.go index aca35b4dd6e2..c864e270d666 100644 --- a/transport/internet/finalmask/salamander/config.go +++ b/transport/internet/finalmask/salamander/config.go @@ -2,41 +2,15 @@ package salamander import ( "net" - - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/transport/internet/finalmask/salamander/obfs" ) func (c *Config) UDP() { } -func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) { - return raw, nil -} - -func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) { - return raw, nil -} - -func (c *Config) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) { - ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password)) - if err != nil { - return nil, errors.New("salamander err").Base(err) - } - return obfs.WrapPacketConn(raw, ob), nil -} - -func (c *Config) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) { - ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password)) - if err != nil { - return nil, errors.New("salamander err").Base(err) - } - return obfs.WrapPacketConn(raw, ob), nil -} - -func (c *Config) Size() int { - return 0 +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) } -func (c *Config) Serialize([]byte) { +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, first, leaveSize) } diff --git a/transport/internet/finalmask/salamander/config.pb.go b/transport/internet/finalmask/salamander/config.pb.go index 7d572b422aea..ddc4944c0526 100644 --- a/transport/internet/finalmask/salamander/config.pb.go +++ b/transport/internet/finalmask/salamander/config.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.36.10 // protoc v6.33.1 -// source: transport/internet/udpmask/salamander/config.proto +// source: transport/internet/finalmask/salamander/config.proto package salamander @@ -30,7 +30,7 @@ type Config struct { func (x *Config) Reset() { *x = Config{} - mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0] + mi := &file_transport_internet_finalmask_salamander_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -42,7 +42,7 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0] + mi := &file_transport_internet_finalmask_salamander_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -55,7 +55,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP(), []int{0} + return file_transport_internet_finalmask_salamander_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetPassword() string { @@ -65,32 +65,32 @@ func (x *Config) GetPassword() string { return "" } -var File_transport_internet_udpmask_salamander_config_proto protoreflect.FileDescriptor +var File_transport_internet_finalmask_salamander_config_proto protoreflect.FileDescriptor -const file_transport_internet_udpmask_salamander_config_proto_rawDesc = "" + +const file_transport_internet_finalmask_salamander_config_proto_rawDesc = "" + "\n" + - "2transport/internet/udpmask/salamander/config.proto\x12*xray.transport.internet.udpmask.salamander\"$\n" + + "4transport/internet/finalmask/salamander/config.proto\x12,xray.transport.internet.finalmask.salamander\"$\n" + "\x06Config\x12\x1a\n" + - "\bpassword\x18\x01 \x01(\tR\bpasswordB\xa0\x01\n" + - ".com.xray.transport.internet.udpmask.salamanderP\x01Z?github.com/xtls/xray-core/transport/internet/udpmask/salamander\xaa\x02*Xray.Transport.Internet.Udpmask.Salamanderb\x06proto3" + "\bpassword\x18\x01 \x01(\tR\bpasswordB\xa6\x01\n" + + "0com.xray.transport.internet.finalmask.salamanderP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/salamander\xaa\x02,Xray.Transport.Internet.Finalmask.Salamanderb\x06proto3" var ( - file_transport_internet_udpmask_salamander_config_proto_rawDescOnce sync.Once - file_transport_internet_udpmask_salamander_config_proto_rawDescData []byte + file_transport_internet_finalmask_salamander_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_salamander_config_proto_rawDescData []byte ) -func file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP() []byte { - file_transport_internet_udpmask_salamander_config_proto_rawDescOnce.Do(func() { - file_transport_internet_udpmask_salamander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc))) +func file_transport_internet_finalmask_salamander_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_salamander_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_salamander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_salamander_config_proto_rawDesc), len(file_transport_internet_finalmask_salamander_config_proto_rawDesc))) }) - return file_transport_internet_udpmask_salamander_config_proto_rawDescData + return file_transport_internet_finalmask_salamander_config_proto_rawDescData } -var file_transport_internet_udpmask_salamander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_udpmask_salamander_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.transport.internet.udpmask.salamander.Config +var file_transport_internet_finalmask_salamander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_salamander_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.salamander.Config } -var file_transport_internet_udpmask_salamander_config_proto_depIdxs = []int32{ +var file_transport_internet_finalmask_salamander_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 @@ -98,26 +98,26 @@ var file_transport_internet_udpmask_salamander_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for field type_name } -func init() { file_transport_internet_udpmask_salamander_config_proto_init() } -func file_transport_internet_udpmask_salamander_config_proto_init() { - if File_transport_internet_udpmask_salamander_config_proto != nil { +func init() { file_transport_internet_finalmask_salamander_config_proto_init() } +func file_transport_internet_finalmask_salamander_config_proto_init() { + if File_transport_internet_finalmask_salamander_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc)), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_salamander_config_proto_rawDesc), len(file_transport_internet_finalmask_salamander_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_transport_internet_udpmask_salamander_config_proto_goTypes, - DependencyIndexes: file_transport_internet_udpmask_salamander_config_proto_depIdxs, - MessageInfos: file_transport_internet_udpmask_salamander_config_proto_msgTypes, + GoTypes: file_transport_internet_finalmask_salamander_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_salamander_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_salamander_config_proto_msgTypes, }.Build() - File_transport_internet_udpmask_salamander_config_proto = out.File - file_transport_internet_udpmask_salamander_config_proto_goTypes = nil - file_transport_internet_udpmask_salamander_config_proto_depIdxs = nil + File_transport_internet_finalmask_salamander_config_proto = out.File + file_transport_internet_finalmask_salamander_config_proto_goTypes = nil + file_transport_internet_finalmask_salamander_config_proto_depIdxs = nil } diff --git a/transport/internet/finalmask/salamander/config.proto b/transport/internet/finalmask/salamander/config.proto index bed943ab4428..34bd4cefeabe 100644 --- a/transport/internet/finalmask/salamander/config.proto +++ b/transport/internet/finalmask/salamander/config.proto @@ -1,9 +1,9 @@ syntax = "proto3"; -package xray.transport.internet.udpmask.salamander; -option csharp_namespace = "Xray.Transport.Internet.Udpmask.Salamander"; -option go_package = "github.com/xtls/xray-core/transport/internet/udpmask/salamander"; -option java_package = "com.xray.transport.internet.udpmask.salamander"; +package xray.transport.internet.finalmask.salamander; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Salamander"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/salamander"; +option java_package = "com.xray.transport.internet.finalmask.salamander"; option java_multiple_files = true; message Config { diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go new file mode 100644 index 000000000000..154a6aa33312 --- /dev/null +++ b/transport/internet/finalmask/salamander/conn.go @@ -0,0 +1,147 @@ +package salamander + +import ( + "io" + "net" + "sync" + "time" + + "github.com/xtls/xray-core/common/errors" +) + +type obfsPacketConn struct { + first bool + leaveSize int32 + + conn net.PacketConn + obfs *SalamanderObfuscator + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + ob, err := NewSalamanderObfuscator([]byte(c.Password)) + if err != nil { + return nil, errors.New("salamander err").Base(err) + } + + conn := &obfsPacketConn{ + first: first, + leaveSize: leaveSize, + + conn: raw, + obfs: ob, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + return NewConnClient(c, raw, first, leaveSize) +} + +func (c *obfsPacketConn) Size() int32 { + return smSaltLen +} + +func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) + } + + c.obfs.Deobfuscate(c.readBuf[:n], p) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, err + } + + n, addr, err = c.conn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.Size()) { + return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) + } + + c.obfs.Deobfuscate(p[:n], p) + + return n - int(c.Size()), addr, err +} + +func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if c.first { + if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + return 0, errors.New("too many masks") + } + + c.writeMutex.Lock() + + n = copy(c.writeBuf[c.leaveSize+c.Size():], p) + n += int(c.leaveSize) + int(c.Size()) + + c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n]) + + nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + + if err != nil { + c.writeMutex.Unlock() + return 0, err + } + + if nn != n { + c.writeMutex.Unlock() + return 0, errors.New("nn != n") + } + + c.writeMutex.Unlock() + return len(p), nil + } + + c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:]) + + return c.conn.WriteTo(p, addr) +} + +func (c *obfsPacketConn) Close() error { + return c.conn.Close() +} + +func (c *obfsPacketConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *obfsPacketConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *obfsPacketConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/transport/internet/finalmask/salamander/obfs/conn.go b/transport/internet/finalmask/salamander/obfs/conn.go deleted file mode 100644 index 6b97592eba21..000000000000 --- a/transport/internet/finalmask/salamander/obfs/conn.go +++ /dev/null @@ -1,121 +0,0 @@ -package obfs - -import ( - "net" - "sync" - "syscall" - "time" -) - -const udpBufferSize = 2048 // QUIC packets are at most 1500 bytes long, so 2k should be more than enough - -// Obfuscator is the interface that wraps the Obfuscate and Deobfuscate methods. -// Both methods return the number of bytes written to out. -// If a packet is not valid, the methods should return 0. -type Obfuscator interface { - Obfuscate(in, out []byte) int - Deobfuscate(in, out []byte) int -} - -var _ net.PacketConn = (*obfsPacketConn)(nil) - -type obfsPacketConn struct { - Conn net.PacketConn - Obfs Obfuscator - - readBuf []byte - readMutex sync.Mutex - writeBuf []byte - writeMutex sync.Mutex -} - -// obfsPacketConnUDP is a special case of obfsPacketConn that uses a UDPConn -// as the underlying connection. We pass additional methods to quic-go to -// enable UDP-specific optimizations. -type obfsPacketConnUDP struct { - *obfsPacketConn - UDPConn *net.UDPConn -} - -// WrapPacketConn enables obfuscation on a net.PacketConn. -// The obfuscation is transparent to the caller - the n bytes returned by -// ReadFrom and WriteTo are the number of original bytes, not after -// obfuscation/deobfuscation. -func WrapPacketConn(conn net.PacketConn, obfs Obfuscator) net.PacketConn { - opc := &obfsPacketConn{ - Conn: conn, - Obfs: obfs, - readBuf: make([]byte, udpBufferSize), - writeBuf: make([]byte, udpBufferSize), - } - if udpConn, ok := conn.(*net.UDPConn); ok { - return &obfsPacketConnUDP{ - obfsPacketConn: opc, - UDPConn: udpConn, - } - } else { - return opc - } -} - -func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - for { - c.readMutex.Lock() - n, addr, err = c.Conn.ReadFrom(c.readBuf) - if n <= 0 { - c.readMutex.Unlock() - return n, addr, err - } - n = c.Obfs.Deobfuscate(c.readBuf[:n], p) - c.readMutex.Unlock() - if n > 0 || err != nil { - return n, addr, err - } - // Invalid packet, try again - } -} - -func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - c.writeMutex.Lock() - nn := c.Obfs.Obfuscate(p, c.writeBuf) - _, err = c.Conn.WriteTo(c.writeBuf[:nn], addr) - c.writeMutex.Unlock() - if err == nil { - n = len(p) - } - return n, err -} - -func (c *obfsPacketConn) Close() error { - return c.Conn.Close() -} - -func (c *obfsPacketConn) LocalAddr() net.Addr { - return c.Conn.LocalAddr() -} - -func (c *obfsPacketConn) SetDeadline(t time.Time) error { - return c.Conn.SetDeadline(t) -} - -func (c *obfsPacketConn) SetReadDeadline(t time.Time) error { - return c.Conn.SetReadDeadline(t) -} - -func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error { - return c.Conn.SetWriteDeadline(t) -} - -// UDP-specific methods below - -func (c *obfsPacketConnUDP) SetReadBuffer(bytes int) error { - return c.UDPConn.SetReadBuffer(bytes) -} - -func (c *obfsPacketConnUDP) SetWriteBuffer(bytes int) error { - return c.UDPConn.SetWriteBuffer(bytes) -} - -func (c *obfsPacketConnUDP) SyscallConn() (syscall.RawConn, error) { - return c.UDPConn.SyscallConn() -} diff --git a/transport/internet/finalmask/salamander/obfs/salamander_test.go b/transport/internet/finalmask/salamander/obfs/salamander_test.go deleted file mode 100644 index 85eafdcce6d4..000000000000 --- a/transport/internet/finalmask/salamander/obfs/salamander_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package obfs - -import ( - "crypto/rand" - "testing" - - "github.com/stretchr/testify/assert" -) - -func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) { - o, _ := NewSalamanderObfuscator([]byte("average_password")) - in := make([]byte, 1200) - _, _ = rand.Read(in) - out := make([]byte, 2048) - b.ResetTimer() - for i := 0; i < b.N; i++ { - o.Obfuscate(in, out) - } -} - -func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) { - o, _ := NewSalamanderObfuscator([]byte("average_password")) - in := make([]byte, 1200) - _, _ = rand.Read(in) - out := make([]byte, 2048) - b.ResetTimer() - for i := 0; i < b.N; i++ { - o.Deobfuscate(in, out) - } -} - -func TestSalamanderObfuscator(t *testing.T) { - o, _ := NewSalamanderObfuscator([]byte("average_password")) - in := make([]byte, 1200) - oOut := make([]byte, 2048) - dOut := make([]byte, 2048) - for i := 0; i < 1000; i++ { - _, _ = rand.Read(in) - n := o.Obfuscate(in, oOut) - assert.Equal(t, len(in)+smSaltLen, n) - n = o.Deobfuscate(oOut[:n], dOut) - assert.Equal(t, len(in), n) - assert.Equal(t, in, dOut[:n]) - } -} diff --git a/transport/internet/finalmask/salamander/obfs/salamander.go b/transport/internet/finalmask/salamander/salamander.go similarity index 95% rename from transport/internet/finalmask/salamander/obfs/salamander.go rename to transport/internet/finalmask/salamander/salamander.go index 50a3ce26307d..86d92dcdff65 100644 --- a/transport/internet/finalmask/salamander/obfs/salamander.go +++ b/transport/internet/finalmask/salamander/salamander.go @@ -1,4 +1,4 @@ -package obfs +package salamander import ( "fmt" @@ -15,8 +15,6 @@ const ( smKeyLen = blake2b.Size256 ) -var _ Obfuscator = (*SalamanderObfuscator)(nil) - var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen) // SalamanderObfuscator is an obfuscator that obfuscates each packet with diff --git a/transport/internet/finalmask/salamander/salamander_test.go b/transport/internet/finalmask/salamander/salamander_test.go new file mode 100644 index 000000000000..ffd50821809b --- /dev/null +++ b/transport/internet/finalmask/salamander/salamander_test.go @@ -0,0 +1,81 @@ +package salamander_test + +import ( + "crypto/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/xtls/xray-core/transport/internet/finalmask/salamander" +) + +const ( + smSaltLen = 8 +) + +func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) { + o, _ := salamander.NewSalamanderObfuscator([]byte("average_password")) + in := make([]byte, 1200) + _, _ = rand.Read(in) + out := make([]byte, 2048) + b.ResetTimer() + for i := 0; i < b.N; i++ { + o.Obfuscate(in, out) + } +} + +func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) { + o, _ := salamander.NewSalamanderObfuscator([]byte("average_password")) + in := make([]byte, 1200) + _, _ = rand.Read(in) + out := make([]byte, 2048) + b.ResetTimer() + for i := 0; i < b.N; i++ { + o.Deobfuscate(in, out) + } +} + +func TestSalamanderObfuscator(t *testing.T) { + o, _ := salamander.NewSalamanderObfuscator([]byte("average_password")) + in := make([]byte, 1200) + oOut := make([]byte, 2048) + dOut := make([]byte, 2048) + for i := 0; i < 1000; i++ { + _, _ = rand.Read(in) + n := o.Obfuscate(in, oOut) + assert.Equal(t, len(in)+smSaltLen, n) + n = o.Deobfuscate(oOut[:n], dOut) + assert.Equal(t, len(in), n) + assert.Equal(t, in, dOut[:n]) + } +} + +func TestSalamanderInPlace(t *testing.T) { + o, _ := salamander.NewSalamanderObfuscator([]byte("average_password")) + + in := make([]byte, 1200) + out := make([]byte, 2048) + _, _ = rand.Read(in) + o.Obfuscate(in, out) + + out2 := make([]byte, 2048) + copy(out2[smSaltLen:], in) + o.Obfuscate(out2[smSaltLen:], out2) + + dOut := make([]byte, 2048) + o.Deobfuscate(out, dOut) + + o.Deobfuscate(out2, out2) + + assert.Equal(t, in, dOut[:1200]) + assert.Equal(t, in, out2[:1200]) +} + +func TestSalamanderBounce(t *testing.T) { + o, _ := salamander.NewSalamanderObfuscator([]byte("average_password")) + buf := make([]byte, 8) + for i := 0; i < 1000; i++ { + _, _ = rand.Read(buf) + n := o.Deobfuscate(buf, buf) + assert.Equal(t, 0, n) + } +} diff --git a/transport/internet/finalmask/udp_test.go b/transport/internet/finalmask/udp_test.go new file mode 100644 index 000000000000..bc4962ff6437 --- /dev/null +++ b/transport/internet/finalmask/udp_test.go @@ -0,0 +1,129 @@ +package finalmask_test + +import ( + "bytes" + "net" + "testing" + "time" + + "github.com/xtls/xray-core/transport/internet/finalmask" + "github.com/xtls/xray-core/transport/internet/finalmask/header/dns" + "github.com/xtls/xray-core/transport/internet/finalmask/header/srtp" + "github.com/xtls/xray-core/transport/internet/finalmask/header/utp" + "github.com/xtls/xray-core/transport/internet/finalmask/header/wechat" + "github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard" + "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm" + "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original" + "github.com/xtls/xray-core/transport/internet/finalmask/salamander" +) + +func mustSendRecv( + t *testing.T, + from net.PacketConn, + to net.PacketConn, + msg []byte, +) { + t.Helper() + + go func() { + _, err := from.WriteTo(msg, to.LocalAddr()) + if err != nil { + t.Error(err) + } + }() + + buf := make([]byte, 1024) + n, _, err := to.ReadFrom(buf) + if err != nil { + t.Fatal(err) + } + + if n != len(msg) { + t.Fatalf("unexpected size: %d", n) + } + + if !bytes.Equal(buf[:n], msg) { + t.Fatalf("unexpected data") + } +} + +type layerMask struct { + name string + mask finalmask.Udpmask +} + +func TestPacketConnReadWrite(t *testing.T) { + cases := []layerMask{ + { + name: "aes128gcm", + mask: &aes128gcm.Config{Password: "123"}, + }, + { + name: "original", + mask: &original.Config{}, + }, + { + name: "dns", + mask: &dns.Config{Domain: "www.baidu.com"}, + }, + { + name: "srtp", + mask: &srtp.Config{}, + }, + { + name: "utp", + mask: &utp.Config{}, + }, + { + name: "wechat", + mask: &wechat.Config{}, + }, + { + name: "wireguard", + mask: &wireguard.Config{}, + }, + { + name: "salamander", + mask: &salamander.Config{Password: "1234"}, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + mask := c.mask + + maskManager := finalmask.NewUdpmaskManager([]finalmask.Udpmask{mask, mask}) + + client, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer client.Close() + + client, err = maskManager.WrapPacketConnClient(client) + if err != nil { + t.Fatal(err) + } + + server, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer server.Close() + + server, err = maskManager.WrapPacketConnServer(server) + if err != nil { + t.Fatal(err) + } + + _ = client.SetDeadline(time.Now().Add(time.Second)) + _ = server.SetDeadline(time.Now().Add(time.Second)) + + mustSendRecv(t, client, server, []byte("client -> server")) + mustSendRecv(t, server, client, []byte("server -> client")) + + mustSendRecv(t, client, server, []byte{}) + mustSendRecv(t, server, client, []byte{}) + }) + } +} diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go new file mode 100644 index 000000000000..c8b815d37bf8 --- /dev/null +++ b/transport/internet/finalmask/xdns/client.go @@ -0,0 +1,373 @@ +package xdns + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/base32" + "encoding/binary" + "io" + "net" + "sync" + "time" + + "github.com/xtls/xray-core/common/errors" +) + +const ( + numPadding = 3 + numPaddingForPoll = 8 + initPollDelay = 500 * time.Millisecond + maxPollDelay = 10 * time.Second + pollDelayMultiplier = 2.0 + pollLimit = 16 +) + +var base32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding) + +type packet struct { + p []byte + addr net.Addr +} + +type xdnsConnClient struct { + conn net.PacketConn + + clientID []byte + domain Name + + pollChan chan struct{} + readQueue chan *packet + writeQueue chan *packet + + closed bool + mutex sync.Mutex +} + +func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { + if !end { + return nil, errors.New("xdns requires being at the outermost level") + } + + domain, err := ParseName(c.Domain) + if err != nil { + return nil, err + } + + conn := &xdnsConnClient{ + conn: raw, + + clientID: make([]byte, 8), + domain: domain, + + pollChan: make(chan struct{}, pollLimit), + readQueue: make(chan *packet, 128), + writeQueue: make(chan *packet, 128), + } + + rand.Read(conn.clientID) + + go conn.recvLoop() + go conn.sendLoop() + + return conn, nil +} + +func (c *xdnsConnClient) recvLoop() { + for { + if c.closed { + break + } + + var buf [4096]byte + + n, addr, err := c.conn.ReadFrom(buf[:]) + if err != nil { + continue + } + + resp, err := MessageFromWireFormat(buf[:n]) + if err != nil { + continue + } + + payload := dnsResponsePayload(&resp, c.domain) + + r := bytes.NewReader(payload) + anyPacket := false + for { + p, err := nextPacket(r) + if err != nil { + break + } + anyPacket = true + + buf := make([]byte, len(p)) + copy(buf, p) + select { + case c.readQueue <- &packet{ + p: buf, + addr: addr, + }: + default: + } + } + + if anyPacket { + select { + case c.pollChan <- struct{}{}: + default: + } + } + } + + close(c.pollChan) + close(c.readQueue) +} + +func (c *xdnsConnClient) sendLoop() { + var addr net.Addr + + pollDelay := initPollDelay + pollTimer := time.NewTimer(pollDelay) + for { + var p *packet + pollTimerExpired := false + + select { + case p = <-c.writeQueue: + default: + select { + case p = <-c.writeQueue: + case <-c.pollChan: + case <-pollTimer.C: + pollTimerExpired = true + } + } + + if p != nil { + addr = p.addr + + select { + case <-c.pollChan: + default: + } + } else if addr != nil { + encoded, _ := encode(nil, c.clientID, c.domain) + p = &packet{ + p: encoded, + addr: addr, + } + } + + if pollTimerExpired { + pollDelay = time.Duration(float64(pollDelay) * pollDelayMultiplier) + if pollDelay > maxPollDelay { + pollDelay = maxPollDelay + } + } else { + if !pollTimer.Stop() { + <-pollTimer.C + } + pollDelay = initPollDelay + } + pollTimer.Reset(pollDelay) + + if c.closed { + return + } + + if p != nil { + _, _ = c.conn.WriteTo(p.p, p.addr) + } + } +} + +func (c *xdnsConnClient) Size() int32 { + return 0 +} + +func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + packet, ok := <-c.readQueue + if !ok { + return 0, nil, io.EOF + } + n = copy(p, packet.p) + if n != len(packet.p) { + return 0, nil, io.ErrShortBuffer + } + return n, packet.addr, nil +} + +func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return 0, errors.New("xdns closed") + } + + encoded, err := encode(p, c.clientID, c.domain) + if err != nil { + errors.LogDebug(context.Background(), "xdns encode err", err) + return 0, errors.New("xdns encode").Base(err) + } + + select { + case c.writeQueue <- &packet{ + p: encoded, + addr: addr, + }: + return len(p), nil + default: + return 0, errors.New("xdns queue full") + } +} + +func (c *xdnsConnClient) Close() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return nil + } + + c.closed = true + close(c.writeQueue) + + return c.conn.Close() +} + +func (c *xdnsConnClient) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *xdnsConnClient) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *xdnsConnClient) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *xdnsConnClient) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +func encode(p []byte, clientID []byte, domain Name) ([]byte, error) { + var decoded []byte + { + if len(p) >= 224 { + return nil, errors.New("too long") + } + var buf bytes.Buffer + buf.Write(clientID[:]) + n := numPadding + if len(p) == 0 { + n = numPaddingForPoll + } + buf.WriteByte(byte(224 + n)) + _, _ = io.CopyN(&buf, rand.Reader, int64(n)) + if len(p) > 0 { + buf.WriteByte(byte(len(p))) + buf.Write(p) + } + decoded = buf.Bytes() + } + + encoded := make([]byte, base32Encoding.EncodedLen(len(decoded))) + base32Encoding.Encode(encoded, decoded) + encoded = bytes.ToLower(encoded) + labels := chunks(encoded, 63) + labels = append(labels, domain...) + name, err := NewName(labels) + if err != nil { + return nil, err + } + + var id uint16 + _ = binary.Read(rand.Reader, binary.BigEndian, &id) + query := &Message{ + ID: id, + Flags: 0x0100, + Question: []Question{ + { + Name: name, + Type: RRTypeTXT, + Class: ClassIN, + }, + }, + Additional: []RR{ + { + Name: Name{}, + Type: RRTypeOPT, + Class: 4096, + TTL: 0, + Data: []byte{}, + }, + }, + } + + buf, err := query.WireFormat() + if err != nil { + return nil, err + } + + return buf, nil +} + +func chunks(p []byte, n int) [][]byte { + var result [][]byte + for len(p) > 0 { + sz := len(p) + if sz > n { + sz = n + } + result = append(result, p[:sz]) + p = p[sz:] + } + return result +} + +func nextPacket(r *bytes.Reader) ([]byte, error) { + var n uint16 + err := binary.Read(r, binary.BigEndian, &n) + if err != nil { + return nil, err + } + p := make([]byte, n) + _, err = io.ReadFull(r, p) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return p, err +} + +func dnsResponsePayload(resp *Message, domain Name) []byte { + if resp.Flags&0x8000 != 0x8000 { + return nil + } + if resp.Flags&0x000f != RcodeNoError { + return nil + } + + if len(resp.Answer) != 1 { + return nil + } + answer := resp.Answer[0] + + _, ok := answer.Name.TrimSuffix(domain) + if !ok { + return nil + } + + if answer.Type != RRTypeTXT { + return nil + } + payload, err := DecodeRDataTXT(answer.Data) + if err != nil { + return nil + } + + return payload +} diff --git a/transport/internet/finalmask/xdns/config.go b/transport/internet/finalmask/xdns/config.go new file mode 100644 index 000000000000..cf30902aee4d --- /dev/null +++ b/transport/internet/finalmask/xdns/config.go @@ -0,0 +1,16 @@ +package xdns + +import ( + "net" +) + +func (c *Config) UDP() { +} + +func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, end) +} + +func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServer(c, raw, end) +} diff --git a/transport/internet/finalmask/xdns/config.pb.go b/transport/internet/finalmask/xdns/config.pb.go new file mode 100644 index 000000000000..ba55e6e9cec2 --- /dev/null +++ b/transport/internet/finalmask/xdns/config.pb.go @@ -0,0 +1,123 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.33.1 +// source: transport/internet/finalmask/xdns/config.proto + +package xdns + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 `protogen:"open.v1"` + Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_xdns_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_finalmask_xdns_config_proto_msgTypes[0] + if 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_finalmask_xdns_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +var File_transport_internet_finalmask_xdns_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_xdns_config_proto_rawDesc = "" + + "\n" + + ".transport/internet/finalmask/xdns/config.proto\x12&xray.transport.internet.finalmask.xdns\" \n" + + "\x06Config\x12\x16\n" + + "\x06domain\x18\x01 \x01(\tR\x06domainB\x94\x01\n" + + "*com.xray.transport.internet.finalmask.xdnsP\x01Z;github.com/xtls/xray-core/transport/internet/finalmask/xdns\xaa\x02&Xray.Transport.Internet.Finalmask.Xdnsb\x06proto3" + +var ( + file_transport_internet_finalmask_xdns_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_xdns_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_xdns_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_xdns_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_xdns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_xdns_config_proto_rawDesc), len(file_transport_internet_finalmask_xdns_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_xdns_config_proto_rawDescData +} + +var file_transport_internet_finalmask_xdns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_xdns_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.xdns.Config +} +var file_transport_internet_finalmask_xdns_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_finalmask_xdns_config_proto_init() } +func file_transport_internet_finalmask_xdns_config_proto_init() { + if File_transport_internet_finalmask_xdns_config_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_xdns_config_proto_rawDesc), len(file_transport_internet_finalmask_xdns_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_xdns_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_xdns_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_xdns_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_xdns_config_proto = out.File + file_transport_internet_finalmask_xdns_config_proto_goTypes = nil + file_transport_internet_finalmask_xdns_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/xdns/config.proto b/transport/internet/finalmask/xdns/config.proto new file mode 100644 index 000000000000..e1c717709dea --- /dev/null +++ b/transport/internet/finalmask/xdns/config.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.xdns; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Xdns"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/xdns"; +option java_package = "com.xray.transport.internet.finalmask.xdns"; +option java_multiple_files = true; + +message Config { + string domain = 1; +} + diff --git a/transport/internet/finalmask/xdns/dns.go b/transport/internet/finalmask/xdns/dns.go new file mode 100644 index 000000000000..4cdac7cdbc78 --- /dev/null +++ b/transport/internet/finalmask/xdns/dns.go @@ -0,0 +1,575 @@ +// Package dns deals with encoding and decoding DNS wire format. +package xdns + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "strings" +) + +// The maximum number of DNS name compression pointers we are willing to follow. +// Without something like this, infinite loops are possible. +const compressionPointerLimit = 10 + +var ( + // ErrZeroLengthLabel is the error returned for names that contain a + // zero-length label, like "example..com". + ErrZeroLengthLabel = errors.New("name contains a zero-length label") + + // ErrLabelTooLong is the error returned for labels that are longer than + // 63 octets. + ErrLabelTooLong = errors.New("name contains a label longer than 63 octets") + + // ErrNameTooLong is the error returned for names whose encoded + // representation is longer than 255 octets. + ErrNameTooLong = errors.New("name is longer than 255 octets") + + // ErrReservedLabelType is the error returned when reading a label type + // prefix whose two most significant bits are not 00 or 11. + ErrReservedLabelType = errors.New("reserved label type") + + // ErrTooManyPointers is the error returned when reading a compressed + // name that has too many compression pointers. + ErrTooManyPointers = errors.New("too many compression pointers") + + // ErrTrailingBytes is the error returned when bytes remain in the parse + // buffer after parsing a message. + ErrTrailingBytes = errors.New("trailing bytes after message") + + // ErrIntegerOverflow is the error returned when trying to encode an + // integer greater than 65535 into a 16-bit field. + ErrIntegerOverflow = errors.New("integer overflow") +) + +const ( + // https://tools.ietf.org/html/rfc1035#section-3.2.2 + RRTypeTXT = 16 + // https://tools.ietf.org/html/rfc6891#section-6.1.1 + RRTypeOPT = 41 + + // https://tools.ietf.org/html/rfc1035#section-3.2.4 + ClassIN = 1 + + // https://tools.ietf.org/html/rfc1035#section-4.1.1 + RcodeNoError = 0 // a.k.a. NOERROR + RcodeFormatError = 1 // a.k.a. FORMERR + RcodeNameError = 3 // a.k.a. NXDOMAIN + RcodeNotImplemented = 4 // a.k.a. NOTIMPL + // https://tools.ietf.org/html/rfc6891#section-9 + ExtendedRcodeBadVers = 16 // a.k.a. BADVERS +) + +// Name represents a domain name, a sequence of labels each of which is 63 +// octets or less in length. +// +// https://tools.ietf.org/html/rfc1035#section-3.1 +type Name [][]byte + +// NewName returns a Name from a slice of labels, after checking the labels for +// validity. Does not include a zero-length label at the end of the slice. +func NewName(labels [][]byte) (Name, error) { + name := Name(labels) + // https://tools.ietf.org/html/rfc1035#section-2.3.4 + // Various objects and parameters in the DNS have size limits. + // labels 63 octets or less + // names 255 octets or less + for _, label := range labels { + if len(label) == 0 { + return nil, ErrZeroLengthLabel + } + if len(label) > 63 { + return nil, ErrLabelTooLong + } + } + // Check the total length. + builder := newMessageBuilder() + builder.WriteName(name) + if len(builder.Bytes()) > 255 { + return nil, ErrNameTooLong + } + return name, nil +} + +// ParseName returns a new Name from a string of labels separated by dots, after +// checking the name for validity. A single dot at the end of the string is +// ignored. +func ParseName(s string) (Name, error) { + b := bytes.TrimSuffix([]byte(s), []byte(".")) + if len(b) == 0 { + // bytes.Split(b, ".") would return [""] in this case + return NewName([][]byte{}) + } else { + return NewName(bytes.Split(b, []byte("."))) + } +} + +// String returns a reversible string representation of name. Labels are +// separated by dots, and any bytes in a label that are outside the set +// [0-9A-Za-z-] are replaced with a \xXX hex escape sequence. +func (name Name) String() string { + if len(name) == 0 { + return "." + } + + var buf strings.Builder + for i, label := range name { + if i > 0 { + buf.WriteByte('.') + } + for _, b := range label { + if b == '-' || + ('0' <= b && b <= '9') || + ('A' <= b && b <= 'Z') || + ('a' <= b && b <= 'z') { + buf.WriteByte(b) + } else { + fmt.Fprintf(&buf, "\\x%02x", b) + } + } + } + return buf.String() +} + +// TrimSuffix returns a Name with the given suffix removed, if it was present. +// The second return value indicates whether the suffix was present. If the +// suffix was not present, the first return value is nil. +func (name Name) TrimSuffix(suffix Name) (Name, bool) { + if len(name) < len(suffix) { + return nil, false + } + split := len(name) - len(suffix) + fore, aft := name[:split], name[split:] + for i := 0; i < len(aft); i++ { + if !bytes.Equal(bytes.ToLower(aft[i]), bytes.ToLower(suffix[i])) { + return nil, false + } + } + return fore, true +} + +// Message represents a DNS message. +// +// https://tools.ietf.org/html/rfc1035#section-4.1 +type Message struct { + ID uint16 + Flags uint16 + + Question []Question + Answer []RR + Authority []RR + Additional []RR +} + +// Opcode extracts the OPCODE part of the Flags field. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.1 +func (message *Message) Opcode() uint16 { + return (message.Flags >> 11) & 0xf +} + +// Rcode extracts the RCODE part of the Flags field. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.1 +func (message *Message) Rcode() uint16 { + return message.Flags & 0x000f +} + +// Question represents an entry in the question section of a message. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.2 +type Question struct { + Name Name + Type uint16 + Class uint16 +} + +// RR represents a resource record. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.3 +type RR struct { + Name Name + Type uint16 + Class uint16 + TTL uint32 + Data []byte +} + +// readName parses a DNS name from r. It leaves r positioned just after the +// parsed name. +func readName(r io.ReadSeeker) (Name, error) { + var labels [][]byte + // We limit the number of compression pointers we are willing to follow. + numPointers := 0 + // If we followed any compression pointers, we must finally seek to just + // past the first pointer. + var seekTo int64 +loop: + for { + var labelType byte + err := binary.Read(r, binary.BigEndian, &labelType) + if err != nil { + return nil, err + } + + switch labelType & 0xc0 { + case 0x00: + // This is an ordinary label. + // https://tools.ietf.org/html/rfc1035#section-3.1 + length := int(labelType & 0x3f) + if length == 0 { + break loop + } + label := make([]byte, length) + _, err := io.ReadFull(r, label) + if err != nil { + return nil, err + } + labels = append(labels, label) + case 0xc0: + // This is a compression pointer. + // https://tools.ietf.org/html/rfc1035#section-4.1.4 + upper := labelType & 0x3f + var lower byte + err := binary.Read(r, binary.BigEndian, &lower) + if err != nil { + return nil, err + } + offset := (uint16(upper) << 8) | uint16(lower) + + if numPointers == 0 { + // The first time we encounter a pointer, + // remember our position so we can seek back to + // it when done. + seekTo, err = r.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + } + numPointers++ + if numPointers > compressionPointerLimit { + return nil, ErrTooManyPointers + } + + // Follow the pointer and continue. + _, err = r.Seek(int64(offset), io.SeekStart) + if err != nil { + return nil, err + } + default: + // "The 10 and 01 combinations are reserved for future + // use." + return nil, ErrReservedLabelType + } + } + // If we followed any pointers, then seek back to just after the first + // one. + if numPointers > 0 { + _, err := r.Seek(seekTo, io.SeekStart) + if err != nil { + return nil, err + } + } + return NewName(labels) +} + +// readQuestion parses one entry from the Question section. It leaves r +// positioned just after the parsed entry. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.2 +func readQuestion(r io.ReadSeeker) (Question, error) { + var question Question + var err error + question.Name, err = readName(r) + if err != nil { + return question, err + } + for _, ptr := range []*uint16{&question.Type, &question.Class} { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return question, err + } + } + + return question, nil +} + +// readRR parses one resource record. It leaves r positioned just after the +// parsed resource record. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.3 +func readRR(r io.ReadSeeker) (RR, error) { + var rr RR + var err error + rr.Name, err = readName(r) + if err != nil { + return rr, err + } + for _, ptr := range []*uint16{&rr.Type, &rr.Class} { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return rr, err + } + } + err = binary.Read(r, binary.BigEndian, &rr.TTL) + if err != nil { + return rr, err + } + var rdLength uint16 + err = binary.Read(r, binary.BigEndian, &rdLength) + if err != nil { + return rr, err + } + rr.Data = make([]byte, rdLength) + _, err = io.ReadFull(r, rr.Data) + if err != nil { + return rr, err + } + + return rr, nil +} + +// readMessage parses a complete DNS message. It leaves r positioned just after +// the parsed message. +func readMessage(r io.ReadSeeker) (Message, error) { + var message Message + + // Header section + // https://tools.ietf.org/html/rfc1035#section-4.1.1 + var qdCount, anCount, nsCount, arCount uint16 + for _, ptr := range []*uint16{ + &message.ID, &message.Flags, + &qdCount, &anCount, &nsCount, &arCount, + } { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return message, err + } + } + + // Question section + // https://tools.ietf.org/html/rfc1035#section-4.1.2 + for i := 0; i < int(qdCount); i++ { + question, err := readQuestion(r) + if err != nil { + return message, err + } + message.Question = append(message.Question, question) + } + + // Answer, Authority, and Additional sections + // https://tools.ietf.org/html/rfc1035#section-4.1.3 + for _, rec := range []struct { + ptr *[]RR + count uint16 + }{ + {&message.Answer, anCount}, + {&message.Authority, nsCount}, + {&message.Additional, arCount}, + } { + for i := 0; i < int(rec.count); i++ { + rr, err := readRR(r) + if err != nil { + return message, err + } + *rec.ptr = append(*rec.ptr, rr) + } + } + + return message, nil +} + +// MessageFromWireFormat parses a message from buf and returns a Message object. +// It returns ErrTrailingBytes if there are bytes remaining in buf after parsing +// is done. +func MessageFromWireFormat(buf []byte) (Message, error) { + r := bytes.NewReader(buf) + message, err := readMessage(r) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == nil { + // Check for trailing bytes. + _, err = r.ReadByte() + if err == io.EOF { + err = nil + } else if err == nil { + err = ErrTrailingBytes + } + } + return message, err +} + +// messageBuilder manages the state of serializing a DNS message. Its main +// function is to keep track of names already written for the purpose of name +// compression. +type messageBuilder struct { + w bytes.Buffer + nameCache map[string]int +} + +// newMessageBuilder creates a new messageBuilder with an empty name cache. +func newMessageBuilder() *messageBuilder { + return &messageBuilder{ + nameCache: make(map[string]int), + } +} + +// Bytes returns the serialized DNS message as a slice of bytes. +func (builder *messageBuilder) Bytes() []byte { + return builder.w.Bytes() +} + +// WriteName appends name to the in-progress messageBuilder, employing +// compression pointers to previously written names if possible. +func (builder *messageBuilder) WriteName(name Name) { + // https://tools.ietf.org/html/rfc1035#section-3.1 + for i := range name { + // Has this suffix already been encoded in the message? + if ptr, ok := builder.nameCache[name[i:].String()]; ok && ptr&0x3fff == ptr { + // If so, we can write a compression pointer. + binary.Write(&builder.w, binary.BigEndian, uint16(0xc000|ptr)) + return + } + // Not cached; we must encode this label verbatim. Store a cache + // entry pointing to the beginning of it. + builder.nameCache[name[i:].String()] = builder.w.Len() + length := len(name[i]) + if length == 0 || length > 63 { + panic(length) + } + builder.w.WriteByte(byte(length)) + builder.w.Write(name[i]) + } + builder.w.WriteByte(0) +} + +// WriteQuestion appends a Question section entry to the in-progress +// messageBuilder. +func (builder *messageBuilder) WriteQuestion(question *Question) { + // https://tools.ietf.org/html/rfc1035#section-4.1.2 + builder.WriteName(question.Name) + binary.Write(&builder.w, binary.BigEndian, question.Type) + binary.Write(&builder.w, binary.BigEndian, question.Class) +} + +// WriteRR appends a resource record to the in-progress messageBuilder. It +// returns ErrIntegerOverflow if the length of rr.Data does not fit in 16 bits. +func (builder *messageBuilder) WriteRR(rr *RR) error { + // https://tools.ietf.org/html/rfc1035#section-4.1.3 + builder.WriteName(rr.Name) + binary.Write(&builder.w, binary.BigEndian, rr.Type) + binary.Write(&builder.w, binary.BigEndian, rr.Class) + binary.Write(&builder.w, binary.BigEndian, rr.TTL) + rdLength := uint16(len(rr.Data)) + if int(rdLength) != len(rr.Data) { + return ErrIntegerOverflow + } + binary.Write(&builder.w, binary.BigEndian, rdLength) + builder.w.Write(rr.Data) + return nil +} + +// WriteMessage appends a complete DNS message to the in-progress +// messageBuilder. It returns ErrIntegerOverflow if the number of entries in any +// section, or the length of the data in any resource record, does not fit in 16 +// bits. +func (builder *messageBuilder) WriteMessage(message *Message) error { + // Header section + // https://tools.ietf.org/html/rfc1035#section-4.1.1 + binary.Write(&builder.w, binary.BigEndian, message.ID) + binary.Write(&builder.w, binary.BigEndian, message.Flags) + for _, count := range []int{ + len(message.Question), + len(message.Answer), + len(message.Authority), + len(message.Additional), + } { + count16 := uint16(count) + if int(count16) != count { + return ErrIntegerOverflow + } + binary.Write(&builder.w, binary.BigEndian, count16) + } + + // Question section + // https://tools.ietf.org/html/rfc1035#section-4.1.2 + for _, question := range message.Question { + builder.WriteQuestion(&question) + } + + // Answer, Authority, and Additional sections + // https://tools.ietf.org/html/rfc1035#section-4.1.3 + for _, rrs := range [][]RR{message.Answer, message.Authority, message.Additional} { + for _, rr := range rrs { + err := builder.WriteRR(&rr) + if err != nil { + return err + } + } + } + + return nil +} + +// WireFormat encodes a Message as a slice of bytes in DNS wire format. It +// returns ErrIntegerOverflow if the number of entries in any section, or the +// length of the data in any resource record, does not fit in 16 bits. +func (message *Message) WireFormat() ([]byte, error) { + builder := newMessageBuilder() + err := builder.WriteMessage(message) + if err != nil { + return nil, err + } + return builder.Bytes(), nil +} + +// DecodeRDataTXT decodes TXT-DATA (as found in the RDATA for a resource record +// with TYPE=TXT) as a raw byte slice, by concatenating all the +// s it contains. +// +// https://tools.ietf.org/html/rfc1035#section-3.3.14 +func DecodeRDataTXT(p []byte) ([]byte, error) { + var buf bytes.Buffer + for { + if len(p) == 0 { + return nil, io.ErrUnexpectedEOF + } + n := int(p[0]) + p = p[1:] + if len(p) < n { + return nil, io.ErrUnexpectedEOF + } + buf.Write(p[:n]) + p = p[n:] + if len(p) == 0 { + break + } + } + return buf.Bytes(), nil +} + +// EncodeRDataTXT encodes a slice of bytes as TXT-DATA, as appropriate for the +// RDATA of a resource record with TYPE=TXT. No length restriction is enforced +// here; that must be checked at a higher level. +// +// https://tools.ietf.org/html/rfc1035#section-3.3.14 +func EncodeRDataTXT(p []byte) []byte { + // https://tools.ietf.org/html/rfc1035#section-3.3 + // https://tools.ietf.org/html/rfc1035#section-3.3.14 + // TXT data is a sequence of one or more s, where + // is a length octet followed by that number of + // octets. + var buf bytes.Buffer + for len(p) > 255 { + buf.WriteByte(255) + buf.Write(p[:255]) + p = p[255:] + } + // Must write here, even if len(p) == 0, because it's "*one or more* + // s". + buf.WriteByte(byte(len(p))) + buf.Write(p) + return buf.Bytes() +} diff --git a/transport/internet/finalmask/xdns/dns_test.go b/transport/internet/finalmask/xdns/dns_test.go new file mode 100644 index 000000000000..b07f57b9758c --- /dev/null +++ b/transport/internet/finalmask/xdns/dns_test.go @@ -0,0 +1,592 @@ +package xdns + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "testing" +) + +func namesEqual(a, b Name) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !bytes.Equal(a[i], b[i]) { + return false + } + } + return true +} + +func TestName(t *testing.T) { + for _, test := range []struct { + labels [][]byte + err error + s string + }{ + {[][]byte{}, nil, "."}, + {[][]byte{[]byte("test")}, nil, "test"}, + {[][]byte{[]byte("a"), []byte("b"), []byte("c")}, nil, "a.b.c"}, + + {[][]byte{{}}, ErrZeroLengthLabel, ""}, + {[][]byte{[]byte("a"), {}, []byte("c")}, ErrZeroLengthLabel, ""}, + + // 63 octets. + {[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")}, nil, + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"}, + // 64 octets. + {[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF")}, ErrLabelTooLong, ""}, + + // 64+64+64+62 octets. + {[][]byte{ + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"), + }, nil, + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"}, + // 64+64+64+63 octets. + {[][]byte{ + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD"), + }, ErrNameTooLong, ""}, + // 127 one-octet labels. + {[][]byte{ + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, + }, nil, + "0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E"}, + // 128 one-octet labels. + {[][]byte{ + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + }, ErrNameTooLong, ""}, + } { + // Test that NewName returns proper error codes, and otherwise + // returns an equal slice of labels. + name, err := NewName(test.labels) + if err != test.err || (err == nil && !namesEqual(name, test.labels)) { + t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)", + test.labels, name, err, test.labels, test.err) + continue + } + if test.err != nil { + continue + } + + // Test that the string version of the name comes out as + // expected. + s := name.String() + if s != test.s { + t.Errorf("%+q became string %+q, expected %+q", test.labels, s, test.s) + continue + } + + // Test that parsing from a string back to a Name results in the + // original slice of labels. + name, err = ParseName(s) + if err != nil || !namesEqual(name, test.labels) { + t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)", + test.labels, s, name, err, test.labels, nil) + continue + } + // A trailing dot should be ignored. + if !strings.HasSuffix(s, ".") { + dotName, dotErr := ParseName(s + ".") + if dotErr != err || !namesEqual(dotName, name) { + t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)", + test.labels, s+".", dotName, dotErr, name, err) + continue + } + } + } +} + +func TestParseName(t *testing.T) { + for _, test := range []struct { + s string + name Name + err error + }{ + // This case can't be tested by TestName above because String + // will never produce "" (it produces "." instead). + {"", [][]byte{}, nil}, + } { + name, err := ParseName(test.s) + if err != test.err || (err == nil && !namesEqual(name, test.name)) { + t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)", + test.s, name, err, test.name, test.err) + continue + } + } +} + +func unescapeString(s string) ([][]byte, error) { + if s == "." { + return [][]byte{}, nil + } + + var result [][]byte + for _, label := range strings.Split(s, ".") { + var buf bytes.Buffer + i := 0 + for i < len(label) { + switch label[i] { + case '\\': + if i+3 >= len(label) { + return nil, fmt.Errorf("truncated escape sequence at index %v", i) + } + if label[i+1] != 'x' { + return nil, fmt.Errorf("malformed escape sequence at index %v", i) + } + b, err := strconv.ParseUint(string(label[i+2:i+4]), 16, 8) + if err != nil { + return nil, fmt.Errorf("malformed hex sequence at index %v", i+2) + } + buf.WriteByte(byte(b)) + i += 4 + default: + buf.WriteByte(label[i]) + i++ + } + } + result = append(result, buf.Bytes()) + } + return result, nil +} + +func TestNameString(t *testing.T) { + for _, test := range []struct { + name Name + s string + }{ + {[][]byte{}, "."}, + {[][]byte{[]byte("\x00"), []byte("a.b"), []byte("c\nd\\")}, "\\x00.a\\x2eb.c\\x0ad\\x5c"}, + {[][]byte{ + []byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>"), + []byte("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}"), + []byte("~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc"), + []byte("\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb"), + []byte("\xfc\xfd\xfe\xff"), + }, "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c-\\x2e\\x2f0123456789\\x3a\\x3b\\x3c\\x3d\\x3e.\\x3f\\x40ABCDEFGHIJKLMNOPQRSTUVWXYZ\\x5b\\x5c\\x5d\\x5e\\x5f\\x60abcdefghijklmnopqrstuvwxyz\\x7b\\x7c\\x7d.\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc.\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb.\\xfc\\xfd\\xfe\\xff"}, + } { + s := test.name.String() + if s != test.s { + t.Errorf("%+q escaped to %+q, expected %+q", test.name, s, test.s) + continue + } + unescaped, err := unescapeString(s) + if err != nil { + t.Errorf("%+q unescaping %+q resulted in error %v", test.name, s, err) + continue + } + if !namesEqual(Name(unescaped), test.name) { + t.Errorf("%+q roundtripped through %+q to %+q", test.name, s, unescaped) + continue + } + } +} + +func TestNameTrimSuffix(t *testing.T) { + for _, test := range []struct { + name, suffix string + trimmed string + ok bool + }{ + {"", "", ".", true}, + {".", ".", ".", true}, + {"abc", "", "abc", true}, + {"abc", ".", "abc", true}, + {"", "abc", ".", false}, + {".", "abc", ".", false}, + {"example.com", "com", "example", true}, + {"example.com", "net", ".", false}, + {"example.com", "example.com", ".", true}, + {"example.com", "test.com", ".", false}, + {"example.com", "xample.com", ".", false}, + {"example.com", "example", ".", false}, + {"example.com", "COM", "example", true}, + {"EXAMPLE.COM", "com", "EXAMPLE", true}, + } { + tmp, ok := mustParseName(test.name).TrimSuffix(mustParseName(test.suffix)) + trimmed := tmp.String() + if ok != test.ok || trimmed != test.trimmed { + t.Errorf("TrimSuffix %+q %+q returned (%+q, %v), expected (%+q, %v)", + test.name, test.suffix, trimmed, ok, test.trimmed, test.ok) + continue + } + } +} + +func TestReadName(t *testing.T) { + // Good tests. + for _, test := range []struct { + start int64 + end int64 + input string + s string + }{ + // Empty name. + {0, 1, "\x00abcd", "."}, + // No pointers. + {12, 25, "AAAABBBBCCCC\x07example\x03com\x00", "example.com"}, + // Backward pointer. + {25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c", "sub.example.com"}, + // Forward pointer. + {0, 4, "\x01a\xc0\x04\x03bcd\x00", "a.bcd"}, + // Two backwards pointers. + {31, 38, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c\x04sub2\xc0\x19", "sub2.sub.example.com"}, + // Forward then backward pointer. + {25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x1f\x04sub2\xc0\x0c", "sub.sub2.example.com"}, + // Overlapping codons. + {0, 4, "\x01a\xc0\x03bcd\x00", "a.bcd"}, + // Pointer to empty label. + {0, 10, "\x07example\xc0\x0a\x00", "example"}, + {1, 11, "\x00\x07example\xc0\x00", "example"}, + // Pointer to pointer to empty label. + {0, 10, "\x07example\xc0\x0a\xc0\x0c\x00", "example"}, + {1, 11, "\x00\x07example\xc0\x0c\xc0\x00", "example"}, + } { + r := bytes.NewReader([]byte(test.input)) + _, err := r.Seek(test.start, io.SeekStart) + if err != nil { + panic(err) + } + name, err := readName(r) + if err != nil { + t.Errorf("%+q returned error %s", test.input, err) + continue + } + s := name.String() + if s != test.s { + t.Errorf("%+q returned %+q, expected %+q", test.input, s, test.s) + continue + } + cur, _ := r.Seek(0, io.SeekCurrent) + if cur != test.end { + t.Errorf("%+q left offset %d, expected %d", test.input, cur, test.end) + continue + } + } + + // Bad tests. + for _, test := range []struct { + start int64 + input string + err error + }{ + {0, "", io.ErrUnexpectedEOF}, + // Reserved label type. + {0, "\x80example", ErrReservedLabelType}, + // Reserved label type. + {0, "\x40example", ErrReservedLabelType}, + // No Terminating empty label. + {0, "\x07example\x03com", io.ErrUnexpectedEOF}, + // Pointer past end of buffer. + {0, "\x07example\xc0\xff", io.ErrUnexpectedEOF}, + // Pointer to self. + {0, "\x07example\x03com\xc0\x0c", ErrTooManyPointers}, + // Pointer to self with intermediate label. + {0, "\x07example\x03com\xc0\x08", ErrTooManyPointers}, + // Two pointers that point to each other. + {0, "\xc0\x02\xc0\x00", ErrTooManyPointers}, + // Two pointers that point to each other, with intermediate labels. + {0, "\x01a\xc0\x04\x01b\xc0\x00", ErrTooManyPointers}, + // EOF while reading label. + {0, "\x0aexample", io.ErrUnexpectedEOF}, + // EOF before second byte of pointer. + {0, "\xc0", io.ErrUnexpectedEOF}, + {0, "\x07example\xc0", io.ErrUnexpectedEOF}, + } { + r := bytes.NewReader([]byte(test.input)) + _, err := r.Seek(test.start, io.SeekStart) + if err != nil { + panic(err) + } + name, err := readName(r) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != test.err { + t.Errorf("%+q returned (%+q, %v), expected %v", test.input, name, err, test.err) + continue + } + } +} + +func mustParseName(s string) Name { + name, err := ParseName(s) + if err != nil { + panic(err) + } + return name +} + +func questionsEqual(a, b *Question) bool { + if !namesEqual(a.Name, b.Name) { + return false + } + if a.Type != b.Type || a.Class != b.Class { + return false + } + return true +} + +func rrsEqual(a, b *RR) bool { + if !namesEqual(a.Name, b.Name) { + return false + } + if a.Type != b.Type || a.Class != b.Class || a.TTL != b.TTL { + return false + } + if !bytes.Equal(a.Data, b.Data) { + return false + } + return true +} + +func messagesEqual(a, b *Message) bool { + if a.ID != b.ID || a.Flags != b.Flags { + return false + } + if len(a.Question) != len(b.Question) { + return false + } + for i := 0; i < len(a.Question); i++ { + if !questionsEqual(&a.Question[i], &b.Question[i]) { + return false + } + } + for _, rec := range []struct{ rrA, rrB []RR }{ + {a.Answer, b.Answer}, + {a.Authority, b.Authority}, + {a.Additional, b.Additional}, + } { + if len(rec.rrA) != len(rec.rrB) { + return false + } + for i := 0; i < len(rec.rrA); i++ { + if !rrsEqual(&rec.rrA[i], &rec.rrB[i]) { + return false + } + } + } + return true +} + +func TestMessageFromWireFormat(t *testing.T) { + for _, test := range []struct { + buf string + expected Message + err error + }{ + { + "\x12\x34", + Message{}, + io.ErrUnexpectedEOF, + }, + { + "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01", + Message{ + ID: 0x1234, + Flags: 0x0100, + Question: []Question{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + }, + }, + Answer: []RR{}, + Authority: []RR{}, + Additional: []RR{}, + }, + nil, + }, + { + "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01X", + Message{}, + ErrTrailingBytes, + }, + { + "\x12\x34\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x80\x00\x04\xc0\x00\x02\x01", + Message{ + ID: 0x1234, + Flags: 0x8180, + Question: []Question{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + }, + }, + Answer: []RR{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + TTL: 128, + Data: []byte{192, 0, 2, 1}, + }, + }, + Authority: []RR{}, + Additional: []RR{}, + }, + nil, + }, + } { + message, err := MessageFromWireFormat([]byte(test.buf)) + if err != test.err || (err == nil && !messagesEqual(&message, &test.expected)) { + t.Errorf("%+q\nreturned (%+v, %v)\nexpected (%+v, %v)", + test.buf, message, err, test.expected, test.err) + continue + } + } +} + +func TestMessageWireFormatRoundTrip(t *testing.T) { + for _, message := range []Message{ + { + ID: 0x1234, + Flags: 0x0100, + Question: []Question{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + }, + { + Name: mustParseName("www2.example.com"), + Type: 2, + Class: 2, + }, + }, + Answer: []RR{ + { + Name: mustParseName("abc"), + Type: 2, + Class: 3, + TTL: 0xffffffff, + Data: []byte{1}, + }, + { + Name: mustParseName("xyz"), + Type: 2, + Class: 3, + TTL: 255, + Data: []byte{}, + }, + }, + Authority: []RR{ + { + Name: mustParseName("."), + Type: 65535, + Class: 65535, + TTL: 0, + Data: []byte("XXXXXXXXXXXXXXXXXXX"), + }, + }, + Additional: []RR{}, + }, + } { + buf, err := message.WireFormat() + if err != nil { + t.Errorf("%+v cannot make wire format: %v", message, err) + continue + } + message2, err := MessageFromWireFormat(buf) + if err != nil { + t.Errorf("%+q cannot parse wire format: %v", buf, err) + continue + } + if !messagesEqual(&message, &message2) { + t.Errorf("messages unequal\nbefore: %+v\n after: %+v", message, message2) + continue + } + } +} + +func TestDecodeRDataTXT(t *testing.T) { + for _, test := range []struct { + p []byte + decoded []byte + err error + }{ + {[]byte{}, nil, io.ErrUnexpectedEOF}, + {[]byte("\x00"), []byte{}, nil}, + {[]byte("\x01"), nil, io.ErrUnexpectedEOF}, + } { + decoded, err := DecodeRDataTXT(test.p) + if err != test.err || (err == nil && !bytes.Equal(decoded, test.decoded)) { + t.Errorf("%+q\nreturned (%+q, %v)\nexpected (%+q, %v)", + test.p, decoded, err, test.decoded, test.err) + continue + } + } +} + +func TestEncodeRDataTXT(t *testing.T) { + // Encoding 0 bytes needs to return at least a single length octet of + // zero, not an empty slice. + p := make([]byte, 0) + encoded := EncodeRDataTXT(p) + if len(encoded) < 0 { + t.Errorf("EncodeRDataTXT(%v) returned %v", p, encoded) + } + + // 255 bytes should be able to be encoded into 256 bytes. + p = make([]byte, 255) + encoded = EncodeRDataTXT(p) + if len(encoded) > 256 { + t.Errorf("EncodeRDataTXT(%d bytes) returned %d bytes", len(p), len(encoded)) + } +} + +func TestRDataTXTRoundTrip(t *testing.T) { + for _, p := range [][]byte{ + {}, + []byte("\x00"), + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + }, + } { + rdata := EncodeRDataTXT(p) + decoded, err := DecodeRDataTXT(rdata) + if err != nil || !bytes.Equal(decoded, p) { + t.Errorf("%+q returned (%+q, %v)", p, decoded, err) + continue + } + } +} diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go new file mode 100644 index 000000000000..2a5ec6cb8fe3 --- /dev/null +++ b/transport/internet/finalmask/xdns/server.go @@ -0,0 +1,567 @@ +package xdns + +import ( + "bytes" + "context" + "encoding/binary" + "io" + "net" + "sync" + "time" + + "github.com/xtls/xray-core/common/errors" +) + +const ( + idleTimeout = 2 * time.Minute + responseTTL = 60 + maxResponseDelay = 1 * time.Second +) + +var ( + maxUDPPayload = 1280 - 40 - 8 + maxEncodedPayload = computeMaxEncodedPayload(maxUDPPayload) +) + +func clientIDToAddr(clientID [8]byte) *net.UDPAddr { + ip := make(net.IP, 16) + + copy(ip, []byte{0xfd, 0x00, 0, 0, 0, 0, 0, 0}) + copy(ip[8:], clientID[:]) + + return &net.UDPAddr{ + IP: ip, + } +} + +type record struct { + Resp *Message + Addr net.Addr + // ClientID [8]byte + ClientAddr net.Addr +} + +type queue struct { + lash time.Time + queue chan []byte + stash chan []byte +} + +type xdnsConnServer struct { + conn net.PacketConn + + domain Name + + ch chan *record + readQueue chan *packet + writeQueueMap map[string]*queue + + closed bool + mutex sync.Mutex +} + +func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { + if !end { + return nil, errors.New("xdns requires being at the outermost level") + } + + domain, err := ParseName(c.Domain) + if err != nil { + return nil, err + } + + conn := &xdnsConnServer{ + conn: raw, + + domain: domain, + + ch: make(chan *record, 100), + readQueue: make(chan *packet, 128), + writeQueueMap: make(map[string]*queue), + } + + go conn.clean() + go conn.recvLoop() + go conn.sendLoop() + + return conn, nil +} + +func (c *xdnsConnServer) clean() { + f := func() bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return true + } + + now := time.Now() + + for key, q := range c.writeQueueMap { + if now.Sub(q.lash) >= idleTimeout { + close(q.queue) + close(q.stash) + delete(c.writeQueueMap, key) + } + } + + return false + } + + for { + time.Sleep(idleTimeout / 2) + if f() { + return + } + } +} + +func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return nil + } + + q, ok := c.writeQueueMap[addr.String()] + if !ok { + q = &queue{ + queue: make(chan []byte, 128), + stash: make(chan []byte, 1), + } + c.writeQueueMap[addr.String()] = q + } + q.lash = time.Now() + + return q +} + +func (c *xdnsConnServer) stash(queue *queue, p []byte) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return + } + + select { + case queue.stash <- p: + default: + } +} + +func (c *xdnsConnServer) recvLoop() { + for { + if c.closed { + break + } + + var buf [4096]byte + n, addr, err := c.conn.ReadFrom(buf[:]) + if err != nil { + continue + } + + query, err := MessageFromWireFormat(buf[:n]) + if err != nil { + continue + } + + resp, payload := responseFor(&query, c.domain) + + var clientID [8]byte + n = copy(clientID[:], payload) + payload = payload[n:] + if n == len(clientID) { + r := bytes.NewReader(payload) + for { + p, err := nextPacketServer(r) + if err != nil { + break + } + + buf := make([]byte, len(p)) + copy(buf, p) + select { + case c.readQueue <- &packet{ + p: buf, + addr: clientIDToAddr(clientID), + }: + default: + } + } + } else { + if resp != nil && resp.Rcode() == RcodeNoError { + resp.Flags |= RcodeNameError + } + } + + if resp != nil { + select { + case c.ch <- &record{resp, addr, clientIDToAddr(clientID)}: + default: + } + } + } + + close(c.ch) + close(c.readQueue) +} + +func (c *xdnsConnServer) sendLoop() { + var nextRec *record + for { + rec := nextRec + nextRec = nil + + if rec == nil { + var ok bool + rec, ok = <-c.ch + if !ok { + break + } + } + + if rec.Resp.Rcode() == RcodeNoError && len(rec.Resp.Question) == 1 { + rec.Resp.Answer = []RR{ + { + Name: rec.Resp.Question[0].Name, + Type: rec.Resp.Question[0].Type, + Class: rec.Resp.Question[0].Class, + TTL: responseTTL, + Data: nil, + }, + } + + var payload bytes.Buffer + limit := maxEncodedPayload + timer := time.NewTimer(maxResponseDelay) + for { + queue := c.ensureQueue(rec.ClientAddr) + if queue == nil { + return + } + + var p []byte + + select { + case p = <-queue.stash: + default: + select { + case p = <-queue.stash: + case p = <-queue.queue: + default: + select { + case p = <-queue.stash: + case p = <-queue.queue: + case <-timer.C: + case nextRec = <-c.ch: + } + } + } + + timer.Reset(0) + + if len(p) == 0 { + break + } + + limit -= 2 + len(p) + if payload.Len() == 0 { + + } else if limit < 0 { + c.stash(queue, p) + + break + } + + if int(uint16(len(p))) != len(p) { + panic(len(p)) + } + + _ = binary.Write(&payload, binary.BigEndian, uint16(len(p))) + payload.Write(p) + } + timer.Stop() + + rec.Resp.Answer[0].Data = EncodeRDataTXT(payload.Bytes()) + } + + buf, err := rec.Resp.WireFormat() + if err != nil { + continue + } + + if len(buf) > maxUDPPayload { + errors.LogDebug(context.Background(), "xdns server truncate ", len(buf)) + buf = buf[:maxUDPPayload] + buf[2] |= 0x02 + } + + if c.closed { + return + } + + _, _ = c.conn.WriteTo(buf, rec.Addr) + } +} + +func (c *xdnsConnServer) Size() int32 { + return 0 +} + +func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + packet, ok := <-c.readQueue + if !ok { + return 0, nil, io.EOF + } + n = copy(p, packet.p) + if n != len(packet.p) { + return 0, nil, io.ErrShortBuffer + } + return n, packet.addr, nil +} + +func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { + q := c.ensureQueue(addr) + if q == nil { + return 0, errors.New("xdns closed") + } + + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return 0, errors.New("xdns closed") + } + + buf := make([]byte, len(p)) + copy(buf, p) + + select { + case q.queue <- buf: + return len(p), nil + default: + return 0, errors.New("xdns queue full") + } +} + +func (c *xdnsConnServer) Close() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.closed { + return nil + } + + c.closed = true + for key, q := range c.writeQueueMap { + close(q.queue) + close(q.stash) + delete(c.writeQueueMap, key) + } + + return c.conn.Close() +} + +func (c *xdnsConnServer) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *xdnsConnServer) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *xdnsConnServer) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *xdnsConnServer) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +func nextPacketServer(r *bytes.Reader) ([]byte, error) { + eof := func(err error) error { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return err + } + + for { + prefix, err := r.ReadByte() + if err != nil { + return nil, err + } + if prefix >= 224 { + paddingLen := prefix - 224 + _, err := io.CopyN(io.Discard, r, int64(paddingLen)) + if err != nil { + return nil, eof(err) + } + } else { + p := make([]byte, int(prefix)) + _, err = io.ReadFull(r, p) + return p, eof(err) + } + } +} + +func responseFor(query *Message, domain Name) (*Message, []byte) { + resp := &Message{ + ID: query.ID, + Flags: 0x8000, + Question: query.Question, + } + + if query.Flags&0x8000 != 0 { + return nil, nil + } + + payloadSize := 0 + for _, rr := range query.Additional { + if rr.Type != RRTypeOPT { + continue + } + if len(resp.Additional) != 0 { + resp.Flags |= RcodeFormatError + return resp, nil + } + resp.Additional = append(resp.Additional, RR{ + Name: Name{}, + Type: RRTypeOPT, + Class: 4096, + TTL: 0, + Data: []byte{}, + }) + additional := &resp.Additional[0] + + version := (rr.TTL >> 16) & 0xff + if version != 0 { + resp.Flags |= ExtendedRcodeBadVers & 0xf + additional.TTL = (ExtendedRcodeBadVers >> 4) << 24 + return resp, nil + } + + payloadSize = int(rr.Class) + } + if payloadSize < 512 { + payloadSize = 512 + } + + if len(query.Question) != 1 { + resp.Flags |= RcodeFormatError + return resp, nil + } + question := query.Question[0] + + prefix, ok := question.Name.TrimSuffix(domain) + if !ok { + resp.Flags |= RcodeNameError + return resp, nil + } + resp.Flags |= 0x0400 + + if query.Opcode() != 0 { + resp.Flags |= RcodeNotImplemented + return resp, nil + } + + if question.Type != RRTypeTXT { + resp.Flags |= RcodeNameError + return resp, nil + } + + encoded := bytes.ToUpper(bytes.Join(prefix, nil)) + payload := make([]byte, base32Encoding.DecodedLen(len(encoded))) + n, err := base32Encoding.Decode(payload, encoded) + if err != nil { + resp.Flags |= RcodeNameError + return resp, nil + } + payload = payload[:n] + + if payloadSize < maxUDPPayload { + resp.Flags |= RcodeFormatError + return resp, nil + } + + return resp, payload +} + +func computeMaxEncodedPayload(limit int) int { + maxLengthName, err := NewName([][]byte{ + []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + }) + if err != nil { + panic(err) + } + { + n := 0 + for _, label := range maxLengthName { + n += len(label) + 1 + } + n += 1 + if n != 255 { + panic("computeMaxEncodedPayload n != 255") + } + } + + queryLimit := uint16(limit) + if int(queryLimit) != limit { + queryLimit = 0xffff + } + query := &Message{ + Question: []Question{ + { + Name: maxLengthName, + Type: RRTypeTXT, + Class: RRTypeTXT, + }, + }, + + Additional: []RR{ + { + Name: Name{}, + Type: RRTypeOPT, + Class: queryLimit, + TTL: 0, + Data: []byte{}, + }, + }, + } + resp, _ := responseFor(query, [][]byte{}) + + resp.Answer = []RR{ + { + Name: query.Question[0].Name, + Type: query.Question[0].Type, + Class: query.Question[0].Class, + TTL: responseTTL, + Data: nil, + }, + } + + low := 0 + high := 32768 + for low+1 < high { + mid := (low + high) / 2 + resp.Answer[0].Data = EncodeRDataTXT(make([]byte, mid)) + buf, err := resp.WireFormat() + if err != nil { + panic(err) + } + if len(buf) <= limit { + low = mid + } else { + high = mid + } + } + + return low +} diff --git a/transport/internet/header_test.go b/transport/internet/header_test.go deleted file mode 100644 index e87ee78438ca..000000000000 --- a/transport/internet/header_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package internet_test - -import ( - "testing" - - "github.com/xtls/xray-core/common" - . "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/headers/noop" - "github.com/xtls/xray-core/transport/internet/headers/srtp" - "github.com/xtls/xray-core/transport/internet/headers/utp" - "github.com/xtls/xray-core/transport/internet/headers/wechat" - "github.com/xtls/xray-core/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()) - } - } -} diff --git a/transport/internet/headers/dns/config.pb.go b/transport/internet/headers/dns/config.pb.go deleted file mode 100644 index ee522b1f80f7..000000000000 --- a/transport/internet/headers/dns/config.pb.go +++ /dev/null @@ -1,137 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/dns/config.proto - -package dns - -import ( - 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 - - Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` -} - -func (x *Config) Reset() { - *x = Config{} - mi := &file_transport_internet_headers_dns_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_dns_config_proto_msgTypes[0] - if 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_dns_config_proto_rawDescGZIP(), []int{0} -} - -func (x *Config) GetDomain() string { - if x != nil { - return x.Domain - } - return "" -} - -var File_transport_internet_headers_dns_config_proto protoreflect.FileDescriptor - -var file_transport_internet_headers_dns_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, 0x64, 0x6e, 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, 0x64, - 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x8b, 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, 0x64, 0x6e, 0x73, - 0x50, 0x01, 0x5a, 0x38, 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, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x64, 0x6e, 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, 0x44, - 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_transport_internet_headers_dns_config_proto_rawDescOnce sync.Once - file_transport_internet_headers_dns_config_proto_rawDescData = file_transport_internet_headers_dns_config_proto_rawDesc -) - -func file_transport_internet_headers_dns_config_proto_rawDescGZIP() []byte { - file_transport_internet_headers_dns_config_proto_rawDescOnce.Do(func() { - file_transport_internet_headers_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_dns_config_proto_rawDescData) - }) - return file_transport_internet_headers_dns_config_proto_rawDescData -} - -var file_transport_internet_headers_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_transport_internet_headers_dns_config_proto_goTypes = []any{ - (*Config)(nil), // 0: xray.transport.internet.headers.dns.Config -} -var file_transport_internet_headers_dns_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_dns_config_proto_init() } -func file_transport_internet_headers_dns_config_proto_init() { - if File_transport_internet_headers_dns_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_transport_internet_headers_dns_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_transport_internet_headers_dns_config_proto_goTypes, - DependencyIndexes: file_transport_internet_headers_dns_config_proto_depIdxs, - MessageInfos: file_transport_internet_headers_dns_config_proto_msgTypes, - }.Build() - File_transport_internet_headers_dns_config_proto = out.File - file_transport_internet_headers_dns_config_proto_rawDesc = nil - file_transport_internet_headers_dns_config_proto_goTypes = nil - file_transport_internet_headers_dns_config_proto_depIdxs = nil -} diff --git a/transport/internet/headers/dns/config.proto b/transport/internet/headers/dns/config.proto deleted file mode 100644 index a9a44ff463ab..000000000000 --- a/transport/internet/headers/dns/config.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.dns; -option csharp_namespace = "Xray.Transport.Internet.Headers.DNS"; -option go_package = "github.com/xtls/xray-core/transport/internet/headers/dns"; -option java_package = "com.xray.transport.internet.headers.dns"; -option java_multiple_files = true; - -message Config { - string domain = 1; -} - diff --git a/transport/internet/headers/dns/dns.go b/transport/internet/headers/dns/dns.go deleted file mode 100644 index b6345213983b..000000000000 --- a/transport/internet/headers/dns/dns.go +++ /dev/null @@ -1,123 +0,0 @@ -package dns - -import ( - "context" - "encoding/binary" - "errors" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/dice" -) - -type DNS struct { - header []byte -} - -func (d DNS) Size() int32 { - return int32(len(d.header)) -} - -// Serialize implements PacketHeader. -func (d DNS) Serialize(b []byte) { - copy(b, d.header) - binary.BigEndian.PutUint16(b[0:], dice.RollUint16()) // random transaction ID -} - -// NewDNS returns a new DNS instance based on given config. -func NewDNS(ctx context.Context, config interface{}) (interface{}, error) { - var header []byte - - header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID - header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query - header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions - header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs - header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs - header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs - - buf := make([]byte, 0x100) - - off1, err := packDomainName(config.(*Config).Domain+".", buf) - if err != nil { - return nil, err - } - - header = append(header, buf[:off1]...) - - header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A - header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN - - return DNS{ - header: header, - }, nil -} - -// copied from github.com/miekg/dns -func packDomainName(s string, msg []byte) (off1 int, err error) { - off := 0 - ls := len(s) - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // Except for escaped dots (\.), which are normal dots. - // There is also a trailing zero. - - // Emit sequence of counted strings, chopping at dots. - var ( - begin int - bs []byte - ) - for i := 0; i < ls; i++ { - var c byte - if bs == nil { - c = s[i] - } else { - c = bs[i] - } - - switch c { - case '\\': - if off+1 > len(msg) { - return len(msg), errors.New("buffer size too small") - } - - if bs == nil { - bs = []byte(s) - } - - copy(bs[i:ls-1], bs[i+1:]) - ls-- - case '.': - labelLen := i - begin - if labelLen >= 1<<6 { // top two bits of length must be clear - return len(msg), errors.New("bad rdata") - } - - // off can already (we're in a loop) be bigger than len(msg) - // this happens when a name isn't fully qualified - if off+1+labelLen > len(msg) { - return len(msg), errors.New("buffer size too small") - } - - // The following is covered by the length check above. - msg[off] = byte(labelLen) - - if bs == nil { - copy(msg[off+1:], s[begin:i]) - } else { - copy(msg[off+1:], bs[begin:i]) - } - off += 1 + labelLen - begin = i + 1 - default: - } - } - - if off < len(msg) { - msg[off] = 0 - } - - return off + 1, nil -} - -func init() { - common.Must(common.RegisterConfig((*Config)(nil), NewDNS)) -} diff --git a/transport/internet/headers/srtp/config.pb.go b/transport/internet/headers/srtp/config.pb.go deleted file mode 100644 index 817284975221..000000000000 --- a/transport/internet/headers/srtp/config.pb.go +++ /dev/null @@ -1,187 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/srtp/config.proto - -package srtp - -import ( - 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 - - 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{} - 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 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, 0x8e, 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, 0x39, 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, 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 = []any{ - (*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 - } - 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 -} diff --git a/transport/internet/headers/srtp/config.proto b/transport/internet/headers/srtp/config.proto deleted file mode 100644 index 491e299ca56d..000000000000 --- a/transport/internet/headers/srtp/config.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.srtp; -option csharp_namespace = "Xray.Transport.Internet.Headers.Srtp"; -option go_package = "github.com/xtls/xray-core/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; -} diff --git a/transport/internet/headers/srtp/srtp.go b/transport/internet/headers/srtp/srtp.go deleted file mode 100644 index f85b04d78b60..000000000000 --- a/transport/internet/headers/srtp/srtp.go +++ /dev/null @@ -1,37 +0,0 @@ -package srtp - -import ( - "context" - "encoding/binary" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/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)) -} diff --git a/transport/internet/headers/srtp/srtp_test.go b/transport/internet/headers/srtp/srtp_test.go deleted file mode 100644 index e350dba714bd..000000000000 --- a/transport/internet/headers/srtp/srtp_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package srtp_test - -import ( - "context" - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - . "github.com/xtls/xray-core/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()) - } -} diff --git a/transport/internet/headers/tls/config.pb.go b/transport/internet/headers/tls/config.pb.go deleted file mode 100644 index c67ee426af28..000000000000 --- a/transport/internet/headers/tls/config.pb.go +++ /dev/null @@ -1,127 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/tls/config.proto - -package tls - -import ( - 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 PacketConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *PacketConfig) Reset() { - *x = PacketConfig{} - 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 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, 0x8b, 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, 0x38, 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, 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 = []any{ - (*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 - } - 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 -} diff --git a/transport/internet/headers/tls/config.proto b/transport/internet/headers/tls/config.proto deleted file mode 100644 index f939adfc1454..000000000000 --- a/transport/internet/headers/tls/config.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.tls; -option csharp_namespace = "Xray.Transport.Internet.Headers.Tls"; -option go_package = "github.com/xtls/xray-core/transport/internet/headers/tls"; -option java_package = "com.xray.transport.internet.headers.tls"; -option java_multiple_files = true; - -message PacketConfig {} diff --git a/transport/internet/headers/tls/dtls.go b/transport/internet/headers/tls/dtls.go deleted file mode 100644 index dda903c0c085..000000000000 --- a/transport/internet/headers/tls/dtls.go +++ /dev/null @@ -1,55 +0,0 @@ -package tls - -import ( - "context" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/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)) -} diff --git a/transport/internet/headers/tls/dtls_test.go b/transport/internet/headers/tls/dtls_test.go deleted file mode 100644 index 26fd3aab7034..000000000000 --- a/transport/internet/headers/tls/dtls_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package tls_test - -import ( - "context" - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - . "github.com/xtls/xray-core/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()) - } -} diff --git a/transport/internet/headers/utp/config.pb.go b/transport/internet/headers/utp/config.pb.go deleted file mode 100644 index b0ffa60cc60a..000000000000 --- a/transport/internet/headers/utp/config.pb.go +++ /dev/null @@ -1,137 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/utp/config.proto - -package utp - -import ( - 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 - - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *Config) Reset() { - *x = Config{} - 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 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, 0x8b, 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, 0x38, 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, 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 = []any{ - (*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 - } - 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 -} diff --git a/transport/internet/headers/utp/config.proto b/transport/internet/headers/utp/config.proto deleted file mode 100644 index 0b3627d26a66..000000000000 --- a/transport/internet/headers/utp/config.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.utp; -option csharp_namespace = "Xray.Transport.Internet.Headers.Utp"; -option go_package = "github.com/xtls/xray-core/transport/internet/headers/utp"; -option java_package = "com.xray.transport.internet.headers.utp"; -option java_multiple_files = true; - -message Config { - uint32 version = 1; -} diff --git a/transport/internet/headers/utp/utp.go b/transport/internet/headers/utp/utp.go deleted file mode 100644 index 89688068c756..000000000000 --- a/transport/internet/headers/utp/utp.go +++ /dev/null @@ -1,39 +0,0 @@ -package utp - -import ( - "context" - "encoding/binary" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/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)) -} diff --git a/transport/internet/headers/utp/utp_test.go b/transport/internet/headers/utp/utp_test.go deleted file mode 100644 index b98755cd0973..000000000000 --- a/transport/internet/headers/utp/utp_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package utp_test - -import ( - "context" - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - . "github.com/xtls/xray-core/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()) - } -} diff --git a/transport/internet/headers/wechat/config.pb.go b/transport/internet/headers/wechat/config.pb.go deleted file mode 100644 index 489703947e95..000000000000 --- a/transport/internet/headers/wechat/config.pb.go +++ /dev/null @@ -1,128 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/wechat/config.proto - -package wechat - -import ( - 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 VideoConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *VideoConfig) Reset() { - *x = VideoConfig{} - 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 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, 0x94, 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, 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, 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 = []any{ - (*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 - } - 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 -} diff --git a/transport/internet/headers/wechat/config.proto b/transport/internet/headers/wechat/config.proto deleted file mode 100644 index 524c6dc0ff83..000000000000 --- a/transport/internet/headers/wechat/config.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.wechat; -option csharp_namespace = "Xray.Transport.Internet.Headers.Wechat"; -option go_package = "github.com/xtls/xray-core/transport/internet/headers/wechat"; -option java_package = "com.xray.transport.internet.headers.wechat"; -option java_multiple_files = true; - -message VideoConfig {} diff --git a/transport/internet/headers/wechat/wechat.go b/transport/internet/headers/wechat/wechat.go deleted file mode 100644 index ec6f0821978c..000000000000 --- a/transport/internet/headers/wechat/wechat.go +++ /dev/null @@ -1,43 +0,0 @@ -package wechat - -import ( - "context" - "encoding/binary" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/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)) -} diff --git a/transport/internet/headers/wechat/wechat_test.go b/transport/internet/headers/wechat/wechat_test.go deleted file mode 100644 index 5942a7dda03c..000000000000 --- a/transport/internet/headers/wechat/wechat_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package wechat_test - -import ( - "context" - "testing" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - . "github.com/xtls/xray-core/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()) - } -} diff --git a/transport/internet/headers/wireguard/config.pb.go b/transport/internet/headers/wireguard/config.pb.go deleted file mode 100644 index 3746c1aa0216..000000000000 --- a/transport/internet/headers/wireguard/config.pb.go +++ /dev/null @@ -1,129 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 -// source: transport/internet/headers/wireguard/config.proto - -package wireguard - -import ( - 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 WireguardConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *WireguardConfig) Reset() { - *x = WireguardConfig{} - 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 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, 0x9d, 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, 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, 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 = []any{ - (*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 - } - 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 -} diff --git a/transport/internet/headers/wireguard/config.proto b/transport/internet/headers/wireguard/config.proto deleted file mode 100644 index a115797a5c04..000000000000 --- a/transport/internet/headers/wireguard/config.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; - -package xray.transport.internet.headers.wireguard; -option csharp_namespace = "Xray.Transport.Internet.Headers.Wireguard"; -option go_package = "github.com/xtls/xray-core/transport/internet/headers/wireguard"; -option java_package = "com.xray.transport.internet.headers.wireguard"; -option java_multiple_files = true; - -message WireguardConfig {} diff --git a/transport/internet/headers/wireguard/wireguard.go b/transport/internet/headers/wireguard/wireguard.go deleted file mode 100644 index 4ced1bc32aea..000000000000 --- a/transport/internet/headers/wireguard/wireguard.go +++ /dev/null @@ -1,30 +0,0 @@ -package wireguard - -import ( - "context" - - "github.com/xtls/xray-core/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)) -} diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index 45ea580cbe6d..cb801aa4db19 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -176,6 +176,7 @@ func (c *client) dial() error { } pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index) if err != nil { + raw.Close() return errors.New("udphop err").Base(err) } } @@ -183,6 +184,7 @@ func (c *client) dial() error { if c.udpmaskManager != nil { pktConn, err = c.udpmaskManager.WrapPacketConnClient(pktConn) if err != nil { + raw.Close() return errors.New("mask err").Base(err) } } diff --git a/transport/internet/kcp/config.go b/transport/internet/kcp/config.go index a0dfd51f1193..fd51118cd6a3 100644 --- a/transport/internet/kcp/config.go +++ b/transport/internet/kcp/config.go @@ -1,8 +1,6 @@ package kcp import ( - "crypto/cipher" - "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/transport/internet" ) @@ -48,32 +46,12 @@ func (c *Config) GetWriteBufferSize() uint32 { } // 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) GetReadBufferSize() uint32 { +// if c == nil || c.ReadBuffer == nil { +// return 2 * 1024 * 1024 +// } +// return c.ReadBuffer.Size +// } func (c *Config) GetSendingInFlightSize() uint32 { size := c.GetUplinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue()) @@ -95,9 +73,9 @@ func (c *Config) GetReceivingInFlightSize() uint32 { return size } -func (c *Config) GetReceivingBufferSize() uint32 { - return c.GetReadBufferSize() / c.GetMTUValue() -} +// func (c *Config) GetReceivingBufferSize() uint32 { +// return c.GetReadBufferSize() / c.GetMTUValue() +// } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index ffedcfc7e422..90c4b7b807e3 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -204,7 +204,7 @@ type Connection struct { } // NewConnection create a new KCP connection between local and remote. -func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, config *Config) *Connection { +func NewConnection(meta ConnMetadata, writer io.Writer, closer io.Closer, config *Config) *Connection { errors.LogInfo(context.Background(), "#", meta.Conversation, " creating connection to ", meta.RemoteAddr) conn := &Connection{ @@ -215,7 +215,7 @@ func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, con dataOutput: signal.NewNotifier(), Config: config, output: NewRetryableWriter(NewSegmentWriter(writer)), - mss: config.GetMTUValue() - uint32(writer.Overhead()) - DataSegmentOverhead, + mss: config.GetMTUValue() - DataSegmentOverhead, roundTrip: &RoundTripInfo{ rto: 100, minRtt: config.GetTTIValue(), diff --git a/transport/internet/kcp/connection_test.go b/transport/internet/kcp/connection_test.go index daa458204819..81bc6703ec7d 100644 --- a/transport/internet/kcp/connection_test.go +++ b/transport/internet/kcp/connection_test.go @@ -16,9 +16,7 @@ func (NoOpCloser) Close() error { } func TestConnectionReadTimeout(t *testing.T) { - conn := NewConnection(ConnMetadata{Conversation: 1}, &KCPPacketWriter{ - Writer: buf.DiscardBytes, - }, NoOpCloser(0), &Config{}) + conn := NewConnection(ConnMetadata{Conversation: 1}, buf.DiscardBytes, NoOpCloser(0), &Config{}) conn.SetReadDeadline(time.Now().Add(time.Second)) b := make([]byte, 1024) diff --git a/transport/internet/kcp/crypt.go b/transport/internet/kcp/crypt.go deleted file mode 100644 index 2843379a8b3a..000000000000 --- a/transport/internet/kcp/crypt.go +++ /dev/null @@ -1,77 +0,0 @@ -package kcp - -import ( - "crypto/cipher" - "encoding/binary" - "hash/fnv" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" -) - -// 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, errors.New("invalid auth") - } - - length := binary.BigEndian.Uint16(dst[4:6]) - if len(dst)-6 != int(length) { - return nil, errors.New("invalid auth") - } - - return dst[6:], nil -} diff --git a/transport/internet/kcp/crypt_test.go b/transport/internet/kcp/crypt_test.go deleted file mode 100644 index 9b7d6ce6ba42..000000000000 --- a/transport/internet/kcp/crypt_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package kcp_test - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/xtls/xray-core/common" - . "github.com/xtls/xray-core/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) - } -} diff --git a/transport/internet/kcp/cryptreal.go b/transport/internet/kcp/cryptreal.go deleted file mode 100644 index cd33a6f3c6ed..000000000000 --- a/transport/internet/kcp/cryptreal.go +++ /dev/null @@ -1,13 +0,0 @@ -package kcp - -import ( - "crypto/cipher" - "crypto/sha256" - - "github.com/xtls/xray-core/common/crypto" -) - -func NewAEADAESGCMBasedOnSeed(seed string) cipher.AEAD { - hashedSeed := sha256.Sum256([]byte(seed)) - return crypto.NewAesGcm(hashedSeed[:16]) -} diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 0a3968dc4b8c..577ef27b5b0a 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -54,32 +54,32 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err) } - kcpSettings := streamSettings.ProtocolSettings.(*Config) - - header, err := kcpSettings.GetPackerHeader() - if err != nil { - return nil, errors.New("failed to create packet header").Base(err) - } - security, err := kcpSettings.GetSecurity() - if err != nil { - return nil, errors.New("failed to create security").Base(err) - } - reader := &KCPPacketReader{ - Header: header, - Security: security, + wrapper, ok := rawConn.(*internet.PacketConnWrapper) + if !ok { + rawConn.Close() + return nil, errors.New("raw is not PacketConnWrapper") } - writer := &KCPPacketWriter{ - Header: header, - Security: security, - Writer: rawConn, + + raw := wrapper.Conn + + if streamSettings.UdpmaskManager != nil { + wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw) + if err != nil { + raw.Close() + return nil, errors.New("mask err").Base(err) + } } + kcpSettings := streamSettings.ProtocolSettings.(*Config) + + reader := &KCPPacketReader{} + conv := uint16(atomic.AddUint32(&globalConv, 1)) session := NewConnection(ConnMetadata{ LocalAddr: rawConn.LocalAddr(), RemoteAddr: rawConn.RemoteAddr(), Conversation: conv, - }, writer, rawConn, kcpSettings) + }, rawConn, rawConn, kcpSettings) go fetchInput(ctx, rawConn, reader, session) diff --git a/transport/internet/kcp/io.go b/transport/internet/kcp/io.go index 775f47be0c6d..fac9945fb7b7 100644 --- a/transport/internet/kcp/io.go +++ b/transport/internet/kcp/io.go @@ -1,48 +1,12 @@ package kcp -import ( - "crypto/cipher" - "crypto/rand" - "io" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/transport/internet" -) - type PacketReader interface { Read([]byte) []Segment } -type PacketWriter interface { - Overhead() int - io.Writer -} - -type KCPPacketReader struct { - Security cipher.AEAD - Header internet.PacketHeader -} +type KCPPacketReader struct{} 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) @@ -54,42 +18,3 @@ func (r *KCPPacketReader) Read(b []byte) []Segment { } 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 -} diff --git a/transport/internet/kcp/io_test.go b/transport/internet/kcp/io_test.go index 0e9f6291d91d..d63efa669bf4 100644 --- a/transport/internet/kcp/io_test.go +++ b/transport/internet/kcp/io_test.go @@ -7,9 +7,7 @@ import ( ) func TestKCPPacketReader(t *testing.T) { - reader := KCPPacketReader{ - Security: &SimpleAuthenticator{}, - } + reader := KCPPacketReader{} testCases := []struct { Input []byte diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 71ce64628385..aabec65fe1e8 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -2,7 +2,6 @@ package kcp import ( "context" - "crypto/cipher" gotls "crypto/tls" "sync" @@ -30,28 +29,14 @@ type Listener struct { tlsConfig *gotls.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, errors.New("failed to create packet header").Base(err).AtError() - } - security, err := kcpSettings.GetSecurity() - if err != nil { - return nil, errors.New("failed to create security").Base(err).AtError() - } + l := &Listener{ - header: header, - security: security, - reader: &KCPPacketReader{ - Header: header, - Security: security, - }, + reader: &KCPPacketReader{}, sessions: make(map[ConnectionID]*Connection), config: kcpSettings, addConn: addConn, @@ -124,11 +109,7 @@ func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) { LocalAddr: localAddr, RemoteAddr: remoteAddr, Conversation: conv, - }, &KCPPacketWriter{ - Header: l.header, - Security: l.security, - Writer: writer, - }, writer, l.config) + }, writer, writer, l.config) var netConn stat.Connection = conn if l.tlsConfig != nil { netConn = tls.Server(conn, l.tlsConfig) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 08354c43d00d..5d29d203066c 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -25,7 +25,8 @@ func HubReceiveOriginalDestination(r bool) HubOption { } type Hub struct { - conn *net.UDPConn + conn net.PacketConn + udpConn *net.UDPConn cache chan *udp.Packet capacity int recvOrigDest bool @@ -56,15 +57,27 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe hub.recvOrigDest = true } - udpConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{ + var err error + hub.conn, err = internet.ListenSystemPacket(ctx, &net.UDPAddr{ IP: address.IP(), Port: int(port), }, sockopt) if err != nil { return nil, err } + + raw := hub.conn + + if streamSettings.UdpmaskManager != nil { + hub.conn, err = streamSettings.UdpmaskManager.WrapPacketConnServer(raw) + if err != nil { + raw.Close() + return nil, errors.New("mask err").Base(err) + } + } + errors.LogInfo(ctx, "listening UDP on ", address, ":", port) - hub.conn = udpConn.(*net.UDPConn) + hub.udpConn, _ = hub.conn.(*net.UDPConn) hub.cache = make(chan *udp.Packet, hub.capacity) go hub.start() @@ -78,7 +91,7 @@ func (h *Hub) Close() error { } func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { - return h.conn.WriteToUDP(payload, &net.UDPAddr{ + return h.conn.WriteTo(payload, &net.UDPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), }) @@ -93,10 +106,21 @@ func (h *Hub) start() { for { buffer := buf.New() var noob int - var addr *net.UDPAddr + var udpAddr *net.UDPAddr rawBytes := buffer.Extend(buf.Size) - n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes) + var n int + var err error + if h.udpConn != nil { + n, noob, _, udpAddr, err = ReadUDPMsg(h.udpConn, rawBytes, oobBytes) + } else { + var addr net.Addr + n, addr, err = h.conn.ReadFrom(rawBytes) + if err == nil { + udpAddr = addr.(*net.UDPAddr) + } + } + if err != nil { errors.LogInfoInner(context.Background(), err, "failed to read UDP msg") buffer.Release() @@ -111,7 +135,7 @@ func (h *Hub) start() { payload := &udp.Packet{ Payload: buffer, - Source: net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), + Source: net.UDPDestination(net.IPAddress(udpAddr.IP), net.Port(udpAddr.Port)), } if h.recvOrigDest && noob > 0 { payload.Target = RetrieveOriginalDest(oobBytes[:noob])