From 07f3ee780b3e76ad8cbb59354709e7a4bcb4f388 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 5 Feb 2026 22:49:50 +0800 Subject: [PATCH 01/75] header --- common/net/cnc/connection.go | 42 +-- infra/conf/transport_internet.go | 34 ++- .../finalmask/header/custom/config.go | 27 ++ .../finalmask/header/custom/config.pb.go | 197 +++++++++++++ .../finalmask/header/custom/config.proto | 18 ++ .../internet/finalmask/header/custom/tcp.go | 205 +++++++++++++ .../internet/finalmask/header/custom/udp.go | 278 ++++++++++++++++++ transport/internet/finalmask/xicmp/client.go | 6 +- transport/internet/hysteria/dialer.go | 50 +++- transport/internet/hysteria/udphop/conn.go | 30 +- transport/internet/kcp/dialer.go | 61 ++-- transport/internet/splithttp/dialer.go | 2 +- transport/internet/system_dialer.go | 65 +--- transport/internet/tcp/dialer.go | 8 + transport/internet/tcp/hub.go | 15 + transport/internet/udp/dialer.go | 51 +++- 16 files changed, 954 insertions(+), 135 deletions(-) create mode 100644 transport/internet/finalmask/header/custom/config.go create mode 100644 transport/internet/finalmask/header/custom/config.pb.go create mode 100644 transport/internet/finalmask/header/custom/config.proto create mode 100644 transport/internet/finalmask/header/custom/tcp.go create mode 100644 transport/internet/finalmask/header/custom/udp.go diff --git a/common/net/cnc/connection.go b/common/net/cnc/connection.go index bdae5409673b..519918fd47e8 100644 --- a/common/net/cnc/connection.go +++ b/common/net/cnc/connection.go @@ -10,46 +10,46 @@ import ( "github.com/xtls/xray-core/common/signal/done" ) -type ConnectionOption func(*connection) +type ConnectionOption func(*Connection) func ConnectionLocalAddr(a net.Addr) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.local = a } } func ConnectionRemoteAddr(a net.Addr) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.remote = a } } func ConnectionInput(writer io.Writer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.writer = buf.NewWriter(writer) } } func ConnectionInputMulti(writer buf.Writer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.writer = writer } } func ConnectionOutput(reader io.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)} } } func ConnectionOutputMulti(reader buf.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{Reader: reader} } } func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.reader = &buf.BufferedReader{ Reader: reader, Splitter: buf.SplitFirstBytes, @@ -58,13 +58,13 @@ func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { } func ConnectionOnClose(n io.Closer) ConnectionOption { - return func(c *connection) { + return func(c *Connection) { c.onClose = n } } func NewConnection(opts ...ConnectionOption) net.Conn { - c := &connection{ + c := &Connection{ done: done.New(), local: &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, @@ -83,7 +83,7 @@ func NewConnection(opts ...ConnectionOption) net.Conn { return c } -type connection struct { +type Connection struct { reader *buf.BufferedReader writer buf.Writer done *done.Instance @@ -92,17 +92,17 @@ type connection struct { remote net.Addr } -func (c *connection) Read(b []byte) (int, error) { +func (c *Connection) Read(b []byte) (int, error) { return c.reader.Read(b) } // ReadMultiBuffer implements buf.Reader. -func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) { +func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) { return c.reader.ReadMultiBuffer() } // Write implements net.Conn.Write(). -func (c *connection) Write(b []byte) (int, error) { +func (c *Connection) Write(b []byte) (int, error) { if c.done.Done() { return 0, io.ErrClosedPipe } @@ -113,7 +113,7 @@ func (c *connection) Write(b []byte) (int, error) { return l, c.writer.WriteMultiBuffer(mb) } -func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { +func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { if c.done.Done() { buf.ReleaseMulti(mb) return io.ErrClosedPipe @@ -123,7 +123,7 @@ func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { } // Close implements net.Conn.Close(). -func (c *connection) Close() error { +func (c *Connection) Close() error { common.Must(c.done.Close()) common.Interrupt(c.reader) common.Close(c.writer) @@ -135,26 +135,26 @@ func (c *connection) Close() error { } // LocalAddr implements net.Conn.LocalAddr(). -func (c *connection) LocalAddr() net.Addr { +func (c *Connection) LocalAddr() net.Addr { return c.local } // RemoteAddr implements net.Conn.RemoteAddr(). -func (c *connection) RemoteAddr() net.Addr { +func (c *Connection) RemoteAddr() net.Addr { return c.remote } // SetDeadline implements net.Conn.SetDeadline(). -func (c *connection) SetDeadline(t time.Time) error { +func (c *Connection) SetDeadline(t time.Time) error { return nil } // SetReadDeadline implements net.Conn.SetReadDeadline(). -func (c *connection) SetReadDeadline(t time.Time) error { +func (c *Connection) SetReadDeadline(t time.Time) error { return nil } // SetWriteDeadline implements net.Conn.SetWriteDeadline(). -func (c *connection) SetWriteDeadline(t time.Time) error { +func (c *Connection) SetWriteDeadline(t time.Time) error { return nil } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 965a05a0b007..5fe7b6fffc39 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -18,6 +18,7 @@ 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/custom" "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" @@ -1237,6 +1238,10 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { } var ( + tcpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ + "header-custom": func() interface{} { return new(HeaderCustomTCP) }, + }, "type", "settings") + udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ "header-dns": func() interface{} { return new(Dns) }, "header-dtls": func() interface{} { return new(Dtls) }, @@ -1244,6 +1249,7 @@ var ( "header-utp": func() interface{} { return new(Utp) }, "header-wechat": func() interface{} { return new(Wechat) }, "header-wireguard": func() interface{} { return new(Wireguard) }, + "header-custom": func() interface{} { return new(HeaderCustomUDP) }, "mkcp-original": func() interface{} { return new(Original) }, "mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) }, "salamander": func() interface{} { return new(Salamander) }, @@ -1252,6 +1258,20 @@ var ( }, "type", "settings") ) +type HeaderCustomTCP struct { + Clients [][]uint8 `json:"clients"` + Servers [][]uint8 `json:"servers"` + OnCloseHeaderError []uint8 `json:"onCloseHeaderError"` +} + +func (c *HeaderCustomTCP) Build() (proto.Message, error) { + return &custom.TCPConfig{ + Clients: c.Clients, + Servers: c.Servers, + OnCloseHeaderError: c.OnCloseHeaderError, + }, nil +} + type Dns struct { Domain string `json:"domain"` } @@ -1297,6 +1317,18 @@ func (c *Wireguard) Build() (proto.Message, error) { return &wireguard.Config{}, nil } +type HeaderCustomUDP struct { + Client []uint8 `json:"client"` + Server []uint8 `json:"server"` +} + +func (c *HeaderCustomUDP) Build() (proto.Message, error) { + return &custom.UDPConfig{ + Client: c.Client, + Server: c.Server, + }, nil +} + type Original struct{} func (c *Original) Build() (proto.Message, error) { @@ -1363,7 +1395,7 @@ type Mask struct { func (c *Mask) Build(tcp bool) (proto.Message, error) { loader := udpmaskLoader if tcp { - return nil, errors.New("") + loader = tcpmaskLoader } settings := []byte("{}") diff --git a/transport/internet/finalmask/header/custom/config.go b/transport/internet/finalmask/header/custom/config.go new file mode 100644 index 000000000000..9e0441027a1f --- /dev/null +++ b/transport/internet/finalmask/header/custom/config.go @@ -0,0 +1,27 @@ +package custom + +import ( + "net" +) + +func (c *TCPConfig) TCP() { +} + +func (c *TCPConfig) WrapConnClient(raw net.Conn) (net.Conn, error) { + return NewConnClientTCP(c, raw) +} + +func (c *TCPConfig) WrapConnServer(raw net.Conn) (net.Conn, error) { + return NewConnServerTCP(c, raw) +} + +func (c *UDPConfig) UDP() { +} + +func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnClientUDP(c, raw, first, leaveSize) +} + +func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { + return NewConnServerUDP(c, raw, first, leaveSize) +} diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go new file mode 100644 index 000000000000..013f3bf7e5f4 --- /dev/null +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -0,0 +1,197 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.33.5 +// source: transport/internet/finalmask/header/custom/config.proto + +package custom + +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 TCPConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Clients [][]byte `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` + Servers [][]byte `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` + OnCloseHeaderError []byte `protobuf:"bytes,3,opt,name=on_close_header_error,json=onCloseHeaderError,proto3" json:"on_close_header_error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TCPConfig) Reset() { + *x = TCPConfig{} + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TCPConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TCPConfig) ProtoMessage() {} + +func (x *TCPConfig) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_custom_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 TCPConfig.ProtoReflect.Descriptor instead. +func (*TCPConfig) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{0} +} + +func (x *TCPConfig) GetClients() [][]byte { + if x != nil { + return x.Clients + } + return nil +} + +func (x *TCPConfig) GetServers() [][]byte { + if x != nil { + return x.Servers + } + return nil +} + +func (x *TCPConfig) GetOnCloseHeaderError() []byte { + if x != nil { + return x.OnCloseHeaderError + } + return nil +} + +type UDPConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Client []byte `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + Server []byte `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UDPConfig) Reset() { + *x = UDPConfig{} + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UDPConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UDPConfig) ProtoMessage() {} + +func (x *UDPConfig) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + 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 UDPConfig.ProtoReflect.Descriptor instead. +func (*UDPConfig) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1} +} + +func (x *UDPConfig) GetClient() []byte { + if x != nil { + return x.Client + } + return nil +} + +func (x *UDPConfig) GetServer() []byte { + if x != nil { + return x.Server + } + return nil +} + +var File_transport_internet_finalmask_header_custom_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" + + "\n" + + "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"r\n" + + "\tTCPConfig\x12\x18\n" + + "\aclients\x18\x01 \x03(\fR\aclients\x12\x18\n" + + "\aservers\x18\x02 \x03(\fR\aservers\x121\n" + + "\x15on_close_header_error\x18\x03 \x01(\fR\x12onCloseHeaderError\";\n" + + "\tUDPConfig\x12\x16\n" + + "\x06client\x18\x01 \x01(\fR\x06client\x12\x16\n" + + "\x06server\x18\x02 \x01(\fR\x06serverB\xaf\x01\n" + + "3com.xray.transport.internet.finalmask.header.customP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/custom\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Customb\x06proto3" + +var ( + file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_header_custom_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_header_custom_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_header_custom_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_header_custom_config_proto_rawDescData +} + +var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any{ + (*TCPConfig)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPConfig + (*UDPConfig)(nil), // 1: xray.transport.internet.finalmask.header.custom.UDPConfig +} +var file_transport_internet_finalmask_header_custom_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_custom_config_proto_init() } +func file_transport_internet_finalmask_header_custom_config_proto_init() { + if File_transport_internet_finalmask_header_custom_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_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_header_custom_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_header_custom_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_header_custom_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_header_custom_config_proto = out.File + file_transport_internet_finalmask_header_custom_config_proto_goTypes = nil + file_transport_internet_finalmask_header_custom_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto new file mode 100644 index 000000000000..f984cb544d48 --- /dev/null +++ b/transport/internet/finalmask/header/custom/config.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.header.custom; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Custom"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/custom"; +option java_package = "com.xray.transport.internet.finalmask.header.custom"; +option java_multiple_files = true; + +message TCPConfig { + repeated bytes clients = 1; + repeated bytes servers = 2; + bytes on_close_header_error = 3; +} + +message UDPConfig { + bytes client = 1; + bytes server = 2; +} \ No newline at end of file diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go new file mode 100644 index 000000000000..c2055ca7d7e6 --- /dev/null +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -0,0 +1,205 @@ +package custom + +import ( + "hash/crc32" + "net" + "sync" + + "github.com/xtls/xray-core/common/errors" +) + +type tcpCustomClient struct { + clients [][]byte + servers [][]byte + checksums []uint32 +} + +type tcpCustomClientConn struct { + net.Conn + header *tcpCustomClient + + auth bool + wg sync.WaitGroup + once sync.Once +} + +func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { + conn := &tcpCustomClientConn{ + Conn: raw, + header: &tcpCustomClient{ + clients: c.Clients, + servers: c.Servers, + }, + } + + for i := range c.Servers { + conn.header.checksums = append(conn.header.checksums, crc32.ChecksumIEEE(c.Servers[i])) + } + + conn.wg.Add(1) + + return conn, nil +} + +func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) { + c.wg.Wait() + + if !c.auth { + return 0, errors.New("header auth failed") + } + + return c.Conn.Read(p) +} + +func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { + c.once.Do(func() { + var buf [8192]byte + + i := 0 + j := 0 + for i = range c.header.clients { + n, err = c.Conn.Write(c.header.clients[i]) + if err != nil { + c.wg.Done() + return + } + if j < len(c.header.checksums) { + n, err = c.Conn.Read(buf[:]) + if err != nil { + c.wg.Done() + return + } + if c.header.checksums[j] != crc32.ChecksumIEEE(buf[:n]) { + c.wg.Done() + return + } + j++ + } + } + + for j < len(c.header.checksums) { + n, err = c.Conn.Read(buf[:]) + if err != nil { + c.wg.Done() + return + } + if c.header.checksums[j] != crc32.ChecksumIEEE(buf[:n]) { + c.wg.Done() + return + } + j++ + } + + c.auth = true + c.wg.Done() + }) + + c.wg.Wait() + + if !c.auth { + return 0, errors.New("header auth failed") + } + + return c.Conn.Write(p) +} + +type tcpCustomServer struct { + clients [][]byte + servers [][]byte + checksums []uint32 + onCloseHeaderError []byte +} + +type tcpCustomServerConn struct { + net.Conn + header *tcpCustomServer + + auth bool + wg sync.WaitGroup + once sync.Once +} + +func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { + conn := &tcpCustomServerConn{ + Conn: raw, + header: &tcpCustomServer{ + clients: c.Clients, + servers: c.Servers, + onCloseHeaderError: c.OnCloseHeaderError, + }, + } + + for i := range c.Clients { + conn.header.checksums = append(conn.header.checksums, crc32.ChecksumIEEE(c.Clients[i])) + } + + conn.wg.Add(1) + + return conn, nil +} + +func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { + c.once.Do(func() { + var buf [8192]byte + + i := 0 + j := 0 + for i = range c.header.checksums { + n, err = c.Conn.Read(buf[:]) + if err != nil { + c.wg.Done() + return + } + if c.header.checksums[i] != crc32.ChecksumIEEE(buf[:n]) { + c.wg.Done() + return + } + if j < len(c.header.servers) { + n, err = c.Conn.Write(c.header.servers[j]) + if err != nil { + c.wg.Done() + return + } + j++ + } + } + + for j < len(c.header.servers) { + n, err = c.Conn.Write(c.header.servers[j]) + if err != nil { + c.wg.Done() + return + } + j++ + } + + c.auth = true + c.wg.Done() + }) + + c.wg.Wait() + + if !c.auth { + return 0, errors.New("header auth failed") + } + + return c.Conn.Read(p) +} + +func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) { + c.wg.Wait() + + if !c.auth { + return 0, errors.New("header auth failed") + } + + return c.Conn.Write(p) +} + +func (c *tcpCustomServerConn) Close() error { + if !c.auth && len(c.header.onCloseHeaderError) > 0 { + c.Conn.Write(c.header.onCloseHeaderError) + } + + return c.Conn.Close() +} diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go new file mode 100644 index 000000000000..611c68cc9e12 --- /dev/null +++ b/transport/internet/finalmask/header/custom/udp.go @@ -0,0 +1,278 @@ +package custom + +import ( + "hash/crc32" + "io" + "net" + sync "sync" + + "github.com/xtls/xray-core/common/errors" +) + +type udpCustomClient struct { + client []byte + server []byte + checksum uint32 + + clientSize int32 + serverSize int32 +} + +func (h *udpCustomClient) Serialize(b []byte) { + copy(b, h.client) +} + +type udpCustomClientConn struct { + first bool + leaveSize int32 + + net.PacketConn + header *udpCustomClient + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &udpCustomClientConn{ + first: first, + leaveSize: leaveSize, + + PacketConn: raw, + header: &udpCustomClient{ + client: c.Client, + server: c.Server, + checksum: crc32.ChecksumIEEE(c.Server), + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func (c *udpCustomClientConn) Size() int32 { + return c.header.clientSize +} + +func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.header.serverSize) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.header.serverSize) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if c.header.checksum != crc32.ChecksumIEEE(c.readBuf[:c.header.serverSize]) { + c.readMutex.Unlock() + return 0, addr, errors.New("checksum mismatch") + } + + copy(p, c.readBuf[c.header.serverSize:n]) + + c.readMutex.Unlock() + return n - int(c.header.serverSize), addr, err + } + + n, addr, err = c.PacketConn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.header.serverSize) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if c.header.checksum != crc32.ChecksumIEEE(p[:c.header.serverSize]) { + return 0, addr, errors.New("checksum mismatch") + } + + copy(p, p[c.header.serverSize:n]) + + return n - int(c.header.serverSize), addr, err +} + +func (c *udpCustomClientConn) 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.PacketConn.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.PacketConn.WriteTo(p, addr) +} + +type udpCustomServer struct { + client []byte + server []byte + checksum uint32 + + clientSize int32 + serverSize int32 +} + +func (h *udpCustomServer) Serialize(b []byte) { + copy(b, h.server) +} + +type udpCustomServerConn struct { + first bool + leaveSize int32 + + net.PacketConn + header *udpCustomServer + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { + conn := &udpCustomServerConn{ + first: first, + leaveSize: leaveSize, + + PacketConn: raw, + header: &udpCustomServer{ + client: c.Client, + server: c.Server, + checksum: crc32.ChecksumIEEE(c.Client), + }, + } + + if first { + conn.readBuf = make([]byte, 8192) + conn.writeBuf = make([]byte, 8192) + } + + return conn, nil +} + +func (c *udpCustomServerConn) Size() int32 { + return c.header.serverSize +} + +func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if c.first { + c.readMutex.Lock() + + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) + if err != nil { + c.readMutex.Unlock() + return n, addr, err + } + + if n < int(c.header.clientSize) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if len(p) < n-int(c.header.clientSize) { + c.readMutex.Unlock() + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if c.header.checksum != crc32.ChecksumIEEE(c.readBuf[:c.header.clientSize]) { + c.readMutex.Unlock() + return 0, addr, errors.New("checksum mismatch") + } + + copy(p, c.readBuf[c.header.clientSize:n]) + + c.readMutex.Unlock() + return n - int(c.header.clientSize), addr, err + } + + n, addr, err = c.PacketConn.ReadFrom(p) + if err != nil { + return n, addr, err + } + + if n < int(c.header.clientSize) { + return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + } + + if c.header.checksum != crc32.ChecksumIEEE(p[:c.header.clientSize]) { + return 0, addr, errors.New("checksum mismatch") + } + + copy(p, p[c.header.clientSize:n]) + + return n - int(c.header.clientSize), addr, err +} + +func (c *udpCustomServerConn) 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.PacketConn.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.PacketConn.WriteTo(p, addr) +} diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 5530861bb4ed..9f02fca34f0f 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -10,6 +10,8 @@ import ( "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/hysteria/udphop" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" @@ -52,7 +54,9 @@ type xicmpConnClient struct { } func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { - if !end { + _, ok1 := raw.(*internet.FakePacketConn) + _, ok2 := raw.(*udphop.UdpHopPacketConn) + if !end || ok1 || ok2 { return nil, errors.New("xicmp requires being at the outermost level") } diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index cb801aa4db19..acecd2903151 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -7,6 +7,7 @@ import ( "math/rand" "net/http" "net/url" + "reflect" "strconv" "sync" "time" @@ -16,6 +17,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/task" hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx" "github.com/xtls/xray-core/transport/internet" @@ -161,17 +163,31 @@ func (c *client) dial() error { return errors.New("failed to dial to dest").Base(err) } - remote := raw.RemoteAddr() + var pktConn net.PacketConn + var remote *net.UDPAddr - pktConn, ok := raw.(net.PacketConn) - if !ok { - raw.Close() - return errors.New("raw is not PacketConn") + switch conn := raw.(type) { + case *internet.PacketConnWrapper: + pktConn = conn.PacketConn + remote = conn.RemoteAddr().(*net.UDPAddr) + case *net.UDPConn: + pktConn = conn + remote = conn.RemoteAddr().(*net.UDPAddr) + case *cnc.Connection: + fakeConn := &internet.FakePacketConn{Conn: conn} + pktConn = fakeConn + remote = fakeConn.RemoteAddr().(*net.UDPAddr) + + if len(c.config.Ports) > 0 { + return errors.New("udphop requires being at the outermost level") + } + default: + return errors.New("unknown conn ", reflect.TypeOf(conn)) } if len(c.config.Ports) > 0 { addr := &udphop.UDPHopAddr{ - IP: remote.(*net.UDPAddr).IP, + IP: remote.IP, Ports: c.config.Ports, } pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index) @@ -338,20 +354,28 @@ func (c *client) udphopDialer(addr *net.UDPAddr) (net.PacketConn, error) { defer c.mutex.Unlock() if c.status() != StatusActive { - errors.LogDebug(c.ctx, "stop hop on disconnected QUIC waiting to be closed") + errors.LogDebug(c.ctx, "skip hop: disconnected QUIC") return nil, errors.New() } - raw, err := internet.DialSystem(c.ctx, net.DestinationFromAddr(addr), c.socketConfig) + raw, err := internet.DialSystem(c.ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), c.socketConfig) if err != nil { - errors.LogDebug(c.ctx, "failed to dial to dest skip hop") + errors.LogDebug(c.ctx, "skip hop: failed to dial to dest") return nil, errors.New() } - pktConn, ok := raw.(net.PacketConn) - if !ok { - errors.LogDebug(c.ctx, "raw is not PacketConn skip hop") - raw.Close() + var pktConn net.PacketConn + + switch conn := raw.(type) { + case *internet.PacketConnWrapper: + pktConn = conn.PacketConn + case *net.UDPConn: + pktConn = conn + case *cnc.Connection: + errors.LogDebug(c.ctx, "skip hop: udphop requires being at the outermost level") + return nil, errors.New() + default: + errors.LogDebug(c.ctx, "skip hop: unknown conn ", reflect.TypeOf(conn)) return nil, errors.New() } diff --git a/transport/internet/hysteria/udphop/conn.go b/transport/internet/hysteria/udphop/conn.go index c19de476150f..663b27e5bdf7 100644 --- a/transport/internet/hysteria/udphop/conn.go +++ b/transport/internet/hysteria/udphop/conn.go @@ -18,7 +18,7 @@ const ( defaultHopInterval = 30 * time.Second ) -type udpHopPacketConn struct { +type UdpHopPacketConn struct { Addr net.Addr Addrs []net.Addr HopIntervalMin int64 @@ -73,7 +73,7 @@ func NewUDPHopPacketConn(addr *UDPHopAddr, intervalMin int64, intervalMax int64, // if err != nil { // return nil, err // } - hConn := &udpHopPacketConn{ + hConn := &UdpHopPacketConn{ Addr: addr, Addrs: addrs, HopIntervalMin: intervalMin, @@ -95,7 +95,7 @@ func NewUDPHopPacketConn(addr *UDPHopAddr, intervalMin int64, intervalMax int64, return hConn, nil } -func (u *udpHopPacketConn) recvLoop(conn net.PacketConn) { +func (u *UdpHopPacketConn) recvLoop(conn net.PacketConn) { for { buf := u.bufPool.Get().([]byte) n, addr, err := conn.ReadFrom(buf) @@ -120,7 +120,7 @@ func (u *udpHopPacketConn) recvLoop(conn net.PacketConn) { } } -func (u *udpHopPacketConn) hopLoop() { +func (u *UdpHopPacketConn) hopLoop() { ticker := time.NewTicker(time.Duration(crypto.RandBetween(u.HopIntervalMin, u.HopIntervalMax)) * time.Second) defer ticker.Stop() for { @@ -134,7 +134,7 @@ func (u *udpHopPacketConn) hopLoop() { } } -func (u *udpHopPacketConn) hop() { +func (u *UdpHopPacketConn) hop() { u.connMutex.Lock() defer u.connMutex.Unlock() if u.closed { @@ -170,7 +170,7 @@ func (u *udpHopPacketConn) hop() { go u.recvLoop(newConn) } -func (u *udpHopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { +func (u *UdpHopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { for { select { case p := <-u.recvQueue: @@ -188,7 +188,7 @@ func (u *udpHopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) } } -func (u *udpHopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { +func (u *UdpHopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { u.connMutex.RLock() defer u.connMutex.RUnlock() if u.closed { @@ -199,7 +199,7 @@ func (u *udpHopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { return u.currentConn.WriteTo(b, u.Addrs[u.addrIndex]) } -func (u *udpHopPacketConn) Close() error { +func (u *UdpHopPacketConn) Close() error { u.connMutex.Lock() defer u.connMutex.Unlock() if u.closed { @@ -218,13 +218,13 @@ func (u *udpHopPacketConn) Close() error { return err } -func (u *udpHopPacketConn) LocalAddr() net.Addr { +func (u *UdpHopPacketConn) LocalAddr() net.Addr { u.connMutex.RLock() defer u.connMutex.RUnlock() return u.currentConn.LocalAddr() } -func (u *udpHopPacketConn) SetDeadline(t time.Time) error { +func (u *UdpHopPacketConn) SetDeadline(t time.Time) error { u.connMutex.RLock() defer u.connMutex.RUnlock() if u.prevConn != nil { @@ -233,7 +233,7 @@ func (u *udpHopPacketConn) SetDeadline(t time.Time) error { return u.currentConn.SetDeadline(t) } -func (u *udpHopPacketConn) SetReadDeadline(t time.Time) error { +func (u *UdpHopPacketConn) SetReadDeadline(t time.Time) error { u.connMutex.RLock() defer u.connMutex.RUnlock() if u.prevConn != nil { @@ -242,7 +242,7 @@ func (u *udpHopPacketConn) SetReadDeadline(t time.Time) error { return u.currentConn.SetReadDeadline(t) } -func (u *udpHopPacketConn) SetWriteDeadline(t time.Time) error { +func (u *UdpHopPacketConn) SetWriteDeadline(t time.Time) error { u.connMutex.RLock() defer u.connMutex.RUnlock() if u.prevConn != nil { @@ -253,7 +253,7 @@ func (u *udpHopPacketConn) SetWriteDeadline(t time.Time) error { // UDP-specific methods below -func (u *udpHopPacketConn) SetReadBuffer(bytes int) error { +func (u *UdpHopPacketConn) SetReadBuffer(bytes int) error { u.connMutex.Lock() defer u.connMutex.Unlock() u.readBufferSize = bytes @@ -263,7 +263,7 @@ func (u *udpHopPacketConn) SetReadBuffer(bytes int) error { return trySetReadBuffer(u.currentConn, bytes) } -func (u *udpHopPacketConn) SetWriteBuffer(bytes int) error { +func (u *UdpHopPacketConn) SetWriteBuffer(bytes int) error { u.connMutex.Lock() defer u.connMutex.Unlock() u.writeBufferSize = bytes @@ -273,7 +273,7 @@ func (u *udpHopPacketConn) SetWriteBuffer(bytes int) error { return trySetWriteBuffer(u.currentConn, bytes) } -func (u *udpHopPacketConn) SyscallConn() (syscall.RawConn, error) { +func (u *UdpHopPacketConn) SyscallConn() (syscall.RawConn, error) { u.connMutex.RLock() defer u.connMutex.RUnlock() sc, ok := u.currentConn.(syscall.Conn) diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index a49155886b22..a5f1a2d2ca8d 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -3,6 +3,7 @@ package kcp import ( "context" "io" + reflect "reflect" "sync/atomic" "github.com/xtls/xray-core/common" @@ -10,6 +11,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -49,24 +51,49 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet dest.Network = net.Network_UDP errors.LogInfo(ctx, "dialing mKCP to ", dest) - rawConn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) + conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err) } if streamSettings.UdpmaskManager != nil { - wrapper, ok := rawConn.(*internet.PacketConnWrapper) - if !ok { - rawConn.Close() - return nil, errors.New("raw is not PacketConnWrapper") - } - - raw := wrapper.Conn - - wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw) - if err != nil { - raw.Close() - return nil, errors.New("mask err").Base(err) + switch c := conn.(type) { + case *internet.PacketConnWrapper: + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(c.PacketConn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + c.PacketConn = pktConn + case *net.UDPConn: + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(c) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = &internet.PacketConnWrapper{ + PacketConn: pktConn, + Dest: &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + } + case *cnc.Connection: + fakeConn := &internet.FakePacketConn{Conn: c} + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(fakeConn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = &internet.PacketConnWrapper{ + PacketConn: pktConn, + Dest: &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + } + default: + return nil, errors.New("unknown conn ", reflect.TypeOf(c)) } } @@ -76,12 +103,12 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet conv := uint16(atomic.AddUint32(&globalConv, 1)) session := NewConnection(ConnMetadata{ - LocalAddr: rawConn.LocalAddr(), - RemoteAddr: rawConn.RemoteAddr(), + LocalAddr: conn.LocalAddr(), + RemoteAddr: conn.RemoteAddr(), Conversation: conv, - }, rawConn, rawConn, kcpSettings) + }, conn, conn, kcpSettings) - go fetchInput(ctx, rawConn, reader, session) + go fetchInput(ctx, conn, reader, session) var iConn stat.Connection = session diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 4d02a67169c2..2c92dbd5897c 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -173,7 +173,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea switch c := conn.(type) { case *internet.PacketConnWrapper: var ok bool - udpConn, ok = c.Conn.(*net.UDPConn) + udpConn, ok = c.PacketConn.(*net.UDPConn) if !ok { return nil, errors.New("PacketConnWrapper does not contain a UDP connection") } diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 015675762eec..317efaa79922 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -2,7 +2,6 @@ package internet import ( "context" - "math/rand" "syscall" "time" @@ -83,8 +82,8 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne return nil, err } return &PacketConnWrapper{ - Conn: packetConn, - Dest: destAddr, + PacketConn: packetConn, + Dest: destAddr, }, nil } // Chrome defaults @@ -150,57 +149,21 @@ func (d *DefaultSystemDialer) DestIpAddress() net.IP { } type PacketConnWrapper struct { - Conn net.PacketConn + net.PacketConn Dest net.Addr } -func (c *PacketConnWrapper) Close() error { - return c.Conn.Close() -} - -func (c *PacketConnWrapper) LocalAddr() net.Addr { - return c.Conn.LocalAddr() -} - -func (c *PacketConnWrapper) RemoteAddr() net.Addr { - return c.Dest -} - -func (c *PacketConnWrapper) Write(p []byte) (int, error) { - return c.Conn.WriteTo(p, c.Dest) -} - func (c *PacketConnWrapper) Read(p []byte) (int, error) { - n, _, err := c.Conn.ReadFrom(p) + n, _, err := c.PacketConn.ReadFrom(p) return n, err } -func (c *PacketConnWrapper) WriteTo(p []byte, d net.Addr) (int, error) { - return c.Conn.WriteTo(p, d) -} - -func (c *PacketConnWrapper) ReadFrom(p []byte) (int, net.Addr, error) { - return c.Conn.ReadFrom(p) -} - -func (c *PacketConnWrapper) SetDeadline(t time.Time) error { - return c.Conn.SetDeadline(t) -} - -func (c *PacketConnWrapper) SetReadDeadline(t time.Time) error { - return c.Conn.SetReadDeadline(t) -} - -func (c *PacketConnWrapper) SetWriteDeadline(t time.Time) error { - return c.Conn.SetWriteDeadline(t) +func (c *PacketConnWrapper) Write(p []byte) (int, error) { + return c.PacketConn.WriteTo(p, c.Dest) } -func (c *PacketConnWrapper) SyscallConn() (syscall.RawConn, error) { - sc, ok := c.Conn.(syscall.Conn) - if !ok { - return nil, syscall.EINVAL - } - return sc.SyscallConn() +func (c *PacketConnWrapper) RemoteAddr() net.Addr { + return c.Dest } type SystemDialerAdapter interface { @@ -269,14 +232,8 @@ func (c *FakePacketConn) WriteTo(p []byte, _ net.Addr) (n int, err error) { } func (c *FakePacketConn) LocalAddr() net.Addr { - return &net.TCPAddr{ - IP: net.IP{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))}, - Port: rand.Intn(65536), + return &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, } } - -func (c *FakePacketConn) SetReadBuffer(bytes int) error { - // do nothing, this function is only there to suppress quic-go printing - // random warnings about UDP buffers to stdout - return nil -} diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 5b966a004e0c..314c986c1eff 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -24,6 +24,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me return nil, err } + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(conn) + if err != nil { + return nil, errors.New("mask err").Base(err) + } + conn = newConn + } + if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { mitmServerName := session.MitmServerNameFromContext(ctx) mitmAlpn11 := session.MitmAlpn11FromContext(ctx) diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 759dfc35a6b7..265aa432f2af 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -11,6 +11,7 @@ import ( "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -24,12 +25,16 @@ type Listener struct { authConfig internet.ConnectionAuthenticator config *Config addConn internet.ConnHandler + + tcpMaskManager *finalmask.TcpmaskManager } // ListenTCP creates a new Listener based on configurations. func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { l := &Listener{ addConn: handler, + + tcpMaskManager: streamSettings.TcpmaskManager, } tcpSettings := streamSettings.ProtocolSettings.(*Config) l.config = tcpSettings @@ -108,6 +113,16 @@ func (v *Listener) keepAccepting() { } continue } + + if v.tcpMaskManager != nil { + newConn, err := v.tcpMaskManager.WrapConnClient(conn) + if err != nil { + errors.LogError(context.Background(), errors.New("mask err").Base(err)) + continue + } + conn = newConn + } + go func() { if v.tlsConfig != nil { conn = tls.Server(conn, v.tlsConfig) diff --git a/transport/internet/udp/dialer.go b/transport/internet/udp/dialer.go index af25eb338c0a..4cd07e1a6cbe 100644 --- a/transport/internet/udp/dialer.go +++ b/transport/internet/udp/dialer.go @@ -2,10 +2,12 @@ package udp import ( "context" + reflect "reflect" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -23,18 +25,43 @@ func init() { } if streamSettings != nil && streamSettings.UdpmaskManager != nil { - wrapper, ok := conn.(*internet.PacketConnWrapper) - if !ok { - conn.Close() - return nil, errors.New("conn is not PacketConnWrapper") - } - - raw := wrapper.Conn - - wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw) - if err != nil { - raw.Close() - return nil, errors.New("mask err").Base(err) + switch c := conn.(type) { + case *internet.PacketConnWrapper: + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(c.PacketConn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + c.PacketConn = pktConn + case *net.UDPConn: + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(c) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = &internet.PacketConnWrapper{ + PacketConn: pktConn, + Dest: &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + } + case *cnc.Connection: + fakeConn := &internet.FakePacketConn{Conn: c} + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(fakeConn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = &internet.PacketConnWrapper{ + PacketConn: pktConn, + Dest: &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + } + default: + return nil, errors.New("unknown conn ", reflect.TypeOf(c)) } } From 3f059780cd3406c8c1010c13e3dadee30b3503f7 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 6 Feb 2026 02:10:21 +0800 Subject: [PATCH 02/75] isTcp --- transport/internet/system_dialer.go | 7 +++++++ transport/internet/tcp/hub.go | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 317efaa79922..16b3e9b0b414 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -237,3 +237,10 @@ func (c *FakePacketConn) LocalAddr() net.Addr { Port: 0, } } + +func (c *FakePacketConn) RemoteAddr() net.Addr { + return &net.UDPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + } +} diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 265aa432f2af..30dba4885acc 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -26,6 +26,7 @@ type Listener struct { config *Config addConn internet.ConnHandler + isTcp bool tcpMaskManager *finalmask.TcpmaskManager } @@ -67,6 +68,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe return nil, errors.New("failed to listen TCP on ", address, ":", port).Base(err) } errors.LogInfo(ctx, "listening TCP on ", address, ":", port) + l.isTcp = true } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { @@ -114,7 +116,7 @@ func (v *Listener) keepAccepting() { continue } - if v.tcpMaskManager != nil { + if v.isTcp && v.tcpMaskManager != nil { newConn, err := v.tcpMaskManager.WrapConnClient(conn) if err != nil { errors.LogError(context.Background(), errors.New("mask err").Base(err)) From 9c02472a98142e18d4f2709ca282bb5cf18abfb1 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 6 Feb 2026 11:37:25 +0800 Subject: [PATCH 03/75] ReadFull --- transport/internet/finalmask/header/custom/tcp.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index c2055ca7d7e6..67e08ee691cb 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -2,6 +2,7 @@ package custom import ( "hash/crc32" + "io" "net" "sync" @@ -64,7 +65,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { return } if j < len(c.header.checksums) { - n, err = c.Conn.Read(buf[:]) + n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) if err != nil { c.wg.Done() return @@ -78,7 +79,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } for j < len(c.header.checksums) { - n, err = c.Conn.Read(buf[:]) + n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) if err != nil { c.wg.Done() return @@ -145,7 +146,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.checksums { - n, err = c.Conn.Read(buf[:]) + n, err := io.ReadFull(c.Conn, buf[:len(c.header.clients[i])]) if err != nil { c.wg.Done() return From 8f3a064782152e7a852dbad179e94d425d34799c Mon Sep 17 00:00:00 2001 From: null Date: Sat, 7 Feb 2026 01:40:35 +0800 Subject: [PATCH 04/75] fix splice --- proxy/proxy.go | 5 ++- transport/internet/finalmask/finalmask.go | 15 +++++++++ .../internet/finalmask/header/custom/tcp.go | 31 +++++++++++++++---- transport/internet/tcp/hub.go | 5 +-- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 29548d9fb120..74d96856946b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -28,6 +28,7 @@ import ( "github.com/xtls/xray-core/proxy/vless/encryption" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -694,6 +695,7 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { if uc, ok := conn.(*internet.UnixConnWrapper); ok { conn = uc.UnixConn } + conn = finalmask.UnwrapTcpMask(conn) } return conn, readCounter, writerCounter } @@ -791,5 +793,6 @@ func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - return ok1 || ok2 || ok3 + _, ok4 := iConn.(finalmask.TcpMaskConn) + return ok1 || ok2 || ok3 || ok4 } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index d8a289a780e7..cc95cf03907c 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -89,3 +89,18 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) { } return raw, nil } + +type TcpMaskConn interface { + TcpMaskConn() + RawConn() net.Conn +} + +func UnwrapTcpMask(conn net.Conn) net.Conn { + for { + if v, ok := conn.(TcpMaskConn); ok { + conn = v.RawConn() + } else { + return conn + } + } +} diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 67e08ee691cb..bd9b20e7dde7 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -42,6 +42,14 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { return conn, nil } +func (c *tcpCustomClientConn) TcpMaskConn() {} + +func (c *tcpCustomClientConn) RawConn() net.Conn { + c.wg.Wait() + + return c.Conn +} + func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) { c.wg.Wait() @@ -115,9 +123,10 @@ type tcpCustomServerConn struct { net.Conn header *tcpCustomServer - auth bool - wg sync.WaitGroup - once sync.Once + auth bool + wg sync.WaitGroup + once sync.Once + closeOnce sync.Once } func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { @@ -139,6 +148,14 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { return conn, nil } +func (c *tcpCustomServerConn) TcpMaskConn() {} + +func (c *tcpCustomServerConn) RawConn() net.Conn { + c.wg.Wait() + + return c.Conn +} + func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { c.once.Do(func() { var buf [8192]byte @@ -198,9 +215,11 @@ func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) { } func (c *tcpCustomServerConn) Close() error { - if !c.auth && len(c.header.onCloseHeaderError) > 0 { - c.Conn.Write(c.header.onCloseHeaderError) - } + c.closeOnce.Do(func() { + if !c.auth && len(c.header.onCloseHeaderError) > 0 { + c.Conn.Write(c.header.onCloseHeaderError) + } + }) return c.Conn.Close() } diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 30dba4885acc..cb3e55e407b4 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -117,9 +117,10 @@ func (v *Listener) keepAccepting() { } if v.isTcp && v.tcpMaskManager != nil { - newConn, err := v.tcpMaskManager.WrapConnClient(conn) + newConn, err := v.tcpMaskManager.WrapConnServer(conn) if err != nil { - errors.LogError(context.Background(), errors.New("mask err").Base(err)) + errors.LogDebug(context.Background(), errors.New("mask err").Base(err)) + conn.Close() continue } conn = newConn From 59d1e02ac7fafc7b6243da76c2d1a6dfd70588c2 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 7 Feb 2026 01:44:43 +0800 Subject: [PATCH 05/75] infra --- infra/conf/transport_internet.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 5fe7b6fffc39..9067ab82c7cf 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1265,6 +1265,20 @@ type HeaderCustomTCP struct { } func (c *HeaderCustomTCP) Build() (proto.Message, error) { + for _, value := range c.Clients { + if len(value) > 8192 { + return nil, errors.New("len > 8192") + } + } + for _, value := range c.Servers { + if len(value) > 8192 { + return nil, errors.New("len > 8192") + } + } + if len(c.OnCloseHeaderError) > 8192 { + return nil, errors.New("len > 8192") + } + return &custom.TCPConfig{ Clients: c.Clients, Servers: c.Servers, From 5c19456976a4b97457420afb3804655ceb420afb Mon Sep 17 00:00:00 2001 From: null Date: Sat, 7 Feb 2026 12:45:07 +0800 Subject: [PATCH 06/75] test case & fix udp & raw close --- .../internet/finalmask/header/custom/udp.go | 6 + transport/internet/finalmask/tcp_test.go | 105 ++++++++++++++++++ transport/internet/finalmask/udp_test.go | 10 +- transport/internet/hysteria/dialer.go | 2 + transport/internet/kcp/dialer.go | 1 + transport/internet/udp/dialer.go | 1 + 6 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 transport/internet/finalmask/tcp_test.go diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 611c68cc9e12..adacdfa4022b 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -45,6 +45,9 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in client: c.Client, server: c.Server, checksum: crc32.ChecksumIEEE(c.Server), + + clientSize: int32(len(c.Client)), + serverSize: int32(len(c.Server)), }, } @@ -179,6 +182,9 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in client: c.Client, server: c.Server, checksum: crc32.ChecksumIEEE(c.Client), + + clientSize: int32(len(c.Client)), + serverSize: int32(len(c.Server)), }, } diff --git a/transport/internet/finalmask/tcp_test.go b/transport/internet/finalmask/tcp_test.go new file mode 100644 index 000000000000..d70974e9511f --- /dev/null +++ b/transport/internet/finalmask/tcp_test.go @@ -0,0 +1,105 @@ +package finalmask_test + +import ( + "bytes" + "io" + "net" + "testing" + "time" + + "github.com/xtls/xray-core/transport/internet/finalmask" + "github.com/xtls/xray-core/transport/internet/finalmask/header/custom" +) + +func mustSendRecvTcp( + t *testing.T, + from net.Conn, + to net.Conn, + msg []byte, +) { + t.Helper() + + go func() { + _, err := from.Write(msg) + if err != nil { + t.Error(err) + } + }() + + buf := make([]byte, 1024) + n, err := io.ReadFull(to, buf[:len(msg)]) + 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 layerMaskTcp struct { + name string + mask finalmask.Tcpmask +} + +func TestConnReadWrite(t *testing.T) { + cases := []layerMaskTcp{ + { + name: "custom", + mask: &custom.TCPConfig{ + Clients: [][]byte{ + {1}, + }, + Servers: [][]byte{ + {2}, + }, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + mask := c.mask + + maskManager := finalmask.NewTcpmaskManager([]finalmask.Tcpmask{mask}) + + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + client, err := net.Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + + client, err = maskManager.WrapConnClient(client) + if err != nil { + t.Fatal(err) + } + + server, err := ln.Accept() + if err != nil { + t.Fatal(err) + } + + server, err = maskManager.WrapConnServer(server) + if err != nil { + t.Fatal(err) + } + + _ = client.SetDeadline(time.Now().Add(time.Second)) + _ = server.SetDeadline(time.Now().Add(time.Second)) + + mustSendRecvTcp(t, client, server, []byte("client -> server")) + mustSendRecvTcp(t, server, client, []byte("server -> client")) + + mustSendRecvTcp(t, client, server, []byte{}) + mustSendRecvTcp(t, server, client, []byte{}) + }) + } +} diff --git a/transport/internet/finalmask/udp_test.go b/transport/internet/finalmask/udp_test.go index bc4962ff6437..02b6b1cb111b 100644 --- a/transport/internet/finalmask/udp_test.go +++ b/transport/internet/finalmask/udp_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/xtls/xray-core/transport/internet/finalmask" + "github.com/xtls/xray-core/transport/internet/finalmask/header/custom" "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" @@ -82,6 +83,13 @@ func TestPacketConnReadWrite(t *testing.T) { name: "wireguard", mask: &wireguard.Config{}, }, + { + name: "custom", + mask: &custom.UDPConfig{ + Client: []byte{1, 1, 1}, + Server: []byte{}, + }, + }, { name: "salamander", mask: &salamander.Config{Password: "1234"}, @@ -98,7 +106,6 @@ func TestPacketConnReadWrite(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() client, err = maskManager.WrapPacketConnClient(client) if err != nil { @@ -109,7 +116,6 @@ func TestPacketConnReadWrite(t *testing.T) { if err != nil { t.Fatal(err) } - defer server.Close() server, err = maskManager.WrapPacketConnServer(server) if err != nil { diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index acecd2903151..b8df13a340f5 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -179,9 +179,11 @@ func (c *client) dial() error { remote = fakeConn.RemoteAddr().(*net.UDPAddr) if len(c.config.Ports) > 0 { + raw.Close() return errors.New("udphop requires being at the outermost level") } default: + raw.Close() return errors.New("unknown conn ", reflect.TypeOf(conn)) } diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index a5f1a2d2ca8d..708d85ab8c39 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -93,6 +93,7 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet }, } default: + conn.Close() return nil, errors.New("unknown conn ", reflect.TypeOf(c)) } } diff --git a/transport/internet/udp/dialer.go b/transport/internet/udp/dialer.go index 4cd07e1a6cbe..316f8e599092 100644 --- a/transport/internet/udp/dialer.go +++ b/transport/internet/udp/dialer.go @@ -61,6 +61,7 @@ func init() { }, } default: + conn.Close() return nil, errors.New("unknown conn ", reflect.TypeOf(c)) } } From 88a1e260bb6ec7675fd9b999ec1301dba43f406f Mon Sep 17 00:00:00 2001 From: null Date: Sat, 7 Feb 2026 12:51:12 +0800 Subject: [PATCH 07/75] fix dialer --- transport/internet/kcp/dialer.go | 5 +---- transport/internet/udp/dialer.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 708d85ab8c39..310bbd5386e7 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -73,10 +73,7 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet } conn = &internet.PacketConnWrapper{ PacketConn: pktConn, - Dest: &net.UDPAddr{ - IP: []byte{0, 0, 0, 0}, - Port: 0, - }, + Dest: c.RemoteAddr().(*net.UDPAddr), } case *cnc.Connection: fakeConn := &internet.FakePacketConn{Conn: c} diff --git a/transport/internet/udp/dialer.go b/transport/internet/udp/dialer.go index 316f8e599092..c930c3551dc2 100644 --- a/transport/internet/udp/dialer.go +++ b/transport/internet/udp/dialer.go @@ -41,10 +41,7 @@ func init() { } conn = &internet.PacketConnWrapper{ PacketConn: pktConn, - Dest: &net.UDPAddr{ - IP: []byte{0, 0, 0, 0}, - Port: 0, - }, + Dest: c.RemoteAddr().(*net.UDPAddr), } case *cnc.Connection: fakeConn := &internet.FakePacketConn{Conn: c} From f66ed825616a56b50de7623441755cc400fc487f Mon Sep 17 00:00:00 2001 From: null Date: Sat, 7 Feb 2026 13:33:46 +0800 Subject: [PATCH 08/75] 1 --- transport/internet/tcp/dialer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 314c986c1eff..92fa7557f13a 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -27,6 +27,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me if streamSettings.TcpmaskManager != nil { newConn, err := streamSettings.TcpmaskManager.WrapConnClient(conn) if err != nil { + conn.Close() return nil, errors.New("mask err").Base(err) } conn = newConn From 37045b34d5155f133e4c117c2b95787705235b61 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 9 Feb 2026 15:32:21 +0800 Subject: [PATCH 09/75] ServersError --- infra/conf/transport_internet.go | 18 +++++----- .../finalmask/header/custom/config.pb.go | 22 ++++++------ .../finalmask/header/custom/config.proto | 2 +- .../internet/finalmask/header/custom/tcp.go | 34 +++++++------------ 4 files changed, 35 insertions(+), 41 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 9067ab82c7cf..6ad2fdb17284 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1259,9 +1259,9 @@ var ( ) type HeaderCustomTCP struct { - Clients [][]uint8 `json:"clients"` - Servers [][]uint8 `json:"servers"` - OnCloseHeaderError []uint8 `json:"onCloseHeaderError"` + Clients [][]uint8 `json:"clients"` + Servers [][]uint8 `json:"servers"` + ServersError [][]uint8 `json:"serversError"` } func (c *HeaderCustomTCP) Build() (proto.Message, error) { @@ -1275,14 +1275,16 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { return nil, errors.New("len > 8192") } } - if len(c.OnCloseHeaderError) > 8192 { - return nil, errors.New("len > 8192") + for _, value := range c.ServersError { + if len(value) > 8192 { + return nil, errors.New("len > 8192") + } } return &custom.TCPConfig{ - Clients: c.Clients, - Servers: c.Servers, - OnCloseHeaderError: c.OnCloseHeaderError, + Clients: c.Clients, + Servers: c.Servers, + ServersError: c.ServersError, }, nil } diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 013f3bf7e5f4..1b717b9ae4ba 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -22,12 +22,12 @@ const ( ) type TCPConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - Clients [][]byte `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` - Servers [][]byte `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` - OnCloseHeaderError []byte `protobuf:"bytes,3,opt,name=on_close_header_error,json=onCloseHeaderError,proto3" json:"on_close_header_error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Clients [][]byte `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` + Servers [][]byte `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` + ServersError [][]byte `protobuf:"bytes,3,rep,name=serversError,proto3" json:"serversError,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *TCPConfig) Reset() { @@ -74,9 +74,9 @@ func (x *TCPConfig) GetServers() [][]byte { return nil } -func (x *TCPConfig) GetOnCloseHeaderError() []byte { +func (x *TCPConfig) GetServersError() [][]byte { if x != nil { - return x.OnCloseHeaderError + return x.ServersError } return nil } @@ -137,11 +137,11 @@ var File_transport_internet_finalmask_header_custom_config_proto protoreflect.Fi const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" + "\n" + - "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"r\n" + + "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"c\n" + "\tTCPConfig\x12\x18\n" + "\aclients\x18\x01 \x03(\fR\aclients\x12\x18\n" + - "\aservers\x18\x02 \x03(\fR\aservers\x121\n" + - "\x15on_close_header_error\x18\x03 \x01(\fR\x12onCloseHeaderError\";\n" + + "\aservers\x18\x02 \x03(\fR\aservers\x12\"\n" + + "\fserversError\x18\x03 \x03(\fR\fserversError\";\n" + "\tUDPConfig\x12\x16\n" + "\x06client\x18\x01 \x01(\fR\x06client\x12\x16\n" + "\x06server\x18\x02 \x01(\fR\x06serverB\xaf\x01\n" + diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index f984cb544d48..65d298718617 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -9,7 +9,7 @@ option java_multiple_files = true; message TCPConfig { repeated bytes clients = 1; repeated bytes servers = 2; - bytes on_close_header_error = 3; + repeated bytes serversError = 3; } message UDPConfig { diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index bd9b20e7dde7..11e618ed3171 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -113,29 +113,28 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } type tcpCustomServer struct { - clients [][]byte - servers [][]byte - checksums []uint32 - onCloseHeaderError []byte + clients [][]byte + servers [][]byte + serversError [][]byte + checksums []uint32 } type tcpCustomServerConn struct { net.Conn header *tcpCustomServer - auth bool - wg sync.WaitGroup - once sync.Once - closeOnce sync.Once + auth bool + wg sync.WaitGroup + once sync.Once } func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { conn := &tcpCustomServerConn{ Conn: raw, header: &tcpCustomServer{ - clients: c.Clients, - servers: c.Servers, - onCloseHeaderError: c.OnCloseHeaderError, + clients: c.Clients, + servers: c.Servers, + serversError: c.ServersError, }, } @@ -169,6 +168,9 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { return } if c.header.checksums[i] != crc32.ChecksumIEEE(buf[:n]) { + if j < len(c.header.serversError) { + c.Conn.Write(c.header.serversError[j]) + } c.wg.Done() return } @@ -213,13 +215,3 @@ func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) { return c.Conn.Write(p) } - -func (c *tcpCustomServerConn) Close() error { - c.closeOnce.Do(func() { - if !c.auth && len(c.header.onCloseHeaderError) > 0 { - c.Conn.Write(c.header.onCloseHeaderError) - } - }) - - return c.Conn.Close() -} From 1b390c9f31885b5e96bf4717e92fc2b2d073ecaf Mon Sep 17 00:00:00 2001 From: null Date: Mon, 9 Feb 2026 23:05:47 +0800 Subject: [PATCH 10/75] errors --- infra/conf/transport_internet.go | 14 +++++++------- .../internet/finalmask/header/custom/config.pb.go | 12 ++++++------ .../internet/finalmask/header/custom/config.proto | 2 +- transport/internet/finalmask/header/custom/tcp.go | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 6ad2fdb17284..d2813be6abfb 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1259,9 +1259,9 @@ var ( ) type HeaderCustomTCP struct { - Clients [][]uint8 `json:"clients"` - Servers [][]uint8 `json:"servers"` - ServersError [][]uint8 `json:"serversError"` + Clients [][]uint8 `json:"clients"` + Servers [][]uint8 `json:"servers"` + Errors [][]uint8 `json:"errors"` } func (c *HeaderCustomTCP) Build() (proto.Message, error) { @@ -1275,16 +1275,16 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { return nil, errors.New("len > 8192") } } - for _, value := range c.ServersError { + for _, value := range c.Errors { if len(value) > 8192 { return nil, errors.New("len > 8192") } } return &custom.TCPConfig{ - Clients: c.Clients, - Servers: c.Servers, - ServersError: c.ServersError, + Clients: c.Clients, + Servers: c.Servers, + Errors: c.Errors, }, nil } diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 1b717b9ae4ba..853d084340cf 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -25,7 +25,7 @@ type TCPConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Clients [][]byte `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` Servers [][]byte `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` - ServersError [][]byte `protobuf:"bytes,3,rep,name=serversError,proto3" json:"serversError,omitempty"` + Errors [][]byte `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -74,9 +74,9 @@ func (x *TCPConfig) GetServers() [][]byte { return nil } -func (x *TCPConfig) GetServersError() [][]byte { +func (x *TCPConfig) GetErrors() [][]byte { if x != nil { - return x.ServersError + return x.Errors } return nil } @@ -137,11 +137,11 @@ var File_transport_internet_finalmask_header_custom_config_proto protoreflect.Fi const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" + "\n" + - "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"c\n" + + "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"W\n" + "\tTCPConfig\x12\x18\n" + "\aclients\x18\x01 \x03(\fR\aclients\x12\x18\n" + - "\aservers\x18\x02 \x03(\fR\aservers\x12\"\n" + - "\fserversError\x18\x03 \x03(\fR\fserversError\";\n" + + "\aservers\x18\x02 \x03(\fR\aservers\x12\x16\n" + + "\x06errors\x18\x03 \x03(\fR\x06errors\";\n" + "\tUDPConfig\x12\x16\n" + "\x06client\x18\x01 \x01(\fR\x06client\x12\x16\n" + "\x06server\x18\x02 \x01(\fR\x06serverB\xaf\x01\n" + diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index 65d298718617..a0adaefacb64 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -9,7 +9,7 @@ option java_multiple_files = true; message TCPConfig { repeated bytes clients = 1; repeated bytes servers = 2; - repeated bytes serversError = 3; + repeated bytes errors = 3; } message UDPConfig { diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 11e618ed3171..059a1a2de047 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -134,7 +134,7 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { header: &tcpCustomServer{ clients: c.Clients, servers: c.Servers, - serversError: c.ServersError, + serversError: c.Errors, }, } From 6e825f093e9c6c7a78e85b7dd4f37052c365654f Mon Sep 17 00:00:00 2001 From: null Date: Mon, 9 Feb 2026 23:36:49 +0800 Subject: [PATCH 11/75] remove checksums --- .../internet/finalmask/header/custom/tcp.go | 28 +++++---------- .../internet/finalmask/header/custom/udp.go | 34 ++++++++----------- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 059a1a2de047..babdb3617bdc 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -1,7 +1,7 @@ package custom import ( - "hash/crc32" + "bytes" "io" "net" "sync" @@ -10,9 +10,8 @@ import ( ) type tcpCustomClient struct { - clients [][]byte - servers [][]byte - checksums []uint32 + clients [][]byte + servers [][]byte } type tcpCustomClientConn struct { @@ -33,10 +32,6 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for i := range c.Servers { - conn.header.checksums = append(conn.header.checksums, crc32.ChecksumIEEE(c.Servers[i])) - } - conn.wg.Add(1) return conn, nil @@ -72,13 +67,13 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { c.wg.Done() return } - if j < len(c.header.checksums) { + if j < len(c.header.servers) { n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) if err != nil { c.wg.Done() return } - if c.header.checksums[j] != crc32.ChecksumIEEE(buf[:n]) { + if !bytes.Equal(c.header.servers[j], buf[:n]) { c.wg.Done() return } @@ -86,13 +81,13 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } } - for j < len(c.header.checksums) { + for j < len(c.header.servers) { n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) if err != nil { c.wg.Done() return } - if c.header.checksums[j] != crc32.ChecksumIEEE(buf[:n]) { + if !bytes.Equal(c.header.servers[j], buf[:n]) { c.wg.Done() return } @@ -116,7 +111,6 @@ type tcpCustomServer struct { clients [][]byte servers [][]byte serversError [][]byte - checksums []uint32 } type tcpCustomServerConn struct { @@ -138,10 +132,6 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for i := range c.Clients { - conn.header.checksums = append(conn.header.checksums, crc32.ChecksumIEEE(c.Clients[i])) - } - conn.wg.Add(1) return conn, nil @@ -161,13 +151,13 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { i := 0 j := 0 - for i = range c.header.checksums { + for i = range c.header.clients { n, err := io.ReadFull(c.Conn, buf[:len(c.header.clients[i])]) if err != nil { c.wg.Done() return } - if c.header.checksums[i] != crc32.ChecksumIEEE(buf[:n]) { + if !bytes.Equal(c.header.clients[i], buf[:n]) { if j < len(c.header.serversError) { c.Conn.Write(c.header.serversError[j]) } diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index adacdfa4022b..2fa5461c9611 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -1,7 +1,7 @@ package custom import ( - "hash/crc32" + "bytes" "io" "net" sync "sync" @@ -10,9 +10,8 @@ import ( ) type udpCustomClient struct { - client []byte - server []byte - checksum uint32 + client []byte + server []byte clientSize int32 serverSize int32 @@ -42,9 +41,8 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in PacketConn: raw, header: &udpCustomClient{ - client: c.Client, - server: c.Server, - checksum: crc32.ChecksumIEEE(c.Server), + client: c.Client, + server: c.Server, clientSize: int32(len(c.Client)), serverSize: int32(len(c.Server)), @@ -83,9 +81,9 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if c.header.checksum != crc32.ChecksumIEEE(c.readBuf[:c.header.serverSize]) { + if !bytes.Equal(c.header.server, c.readBuf[:c.header.serverSize]) { c.readMutex.Unlock() - return 0, addr, errors.New("checksum mismatch") + return 0, addr, errors.New("header mismatch") } copy(p, c.readBuf[c.header.serverSize:n]) @@ -103,8 +101,8 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if c.header.checksum != crc32.ChecksumIEEE(p[:c.header.serverSize]) { - return 0, addr, errors.New("checksum mismatch") + if !bytes.Equal(c.header.server, p[:c.header.serverSize]) { + return 0, addr, errors.New("header mismatch") } copy(p, p[c.header.serverSize:n]) @@ -147,9 +145,8 @@ func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error } type udpCustomServer struct { - client []byte - server []byte - checksum uint32 + client []byte + server []byte clientSize int32 serverSize int32 @@ -179,9 +176,8 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in PacketConn: raw, header: &udpCustomServer{ - client: c.Client, - server: c.Server, - checksum: crc32.ChecksumIEEE(c.Client), + client: c.Client, + server: c.Server, clientSize: int32(len(c.Client)), serverSize: int32(len(c.Server)), @@ -220,7 +216,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if c.header.checksum != crc32.ChecksumIEEE(c.readBuf[:c.header.clientSize]) { + if !bytes.Equal(c.header.client, c.readBuf[:c.header.clientSize]) { c.readMutex.Unlock() return 0, addr, errors.New("checksum mismatch") } @@ -240,7 +236,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if c.header.checksum != crc32.ChecksumIEEE(p[:c.header.clientSize]) { + if !bytes.Equal(c.header.client, p[:c.header.clientSize]) { return 0, addr, errors.New("checksum mismatch") } From bc21e70e826e9fb754953772312b47738e93cc72 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 11:18:14 +0800 Subject: [PATCH 12/75] tcp custom header --- infra/conf/transport_internet.go | 58 ++++-- .../finalmask/header/custom/config.pb.go | 178 +++++++++++++++--- .../finalmask/header/custom/config.proto | 17 +- .../internet/finalmask/header/custom/tcp.go | 163 +++++++++++----- 4 files changed, 329 insertions(+), 87 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index b93ba18fcd25..3df7da01a598 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1298,33 +1298,65 @@ var ( }, "type", "settings") ) +type TCPItem struct { + Delay Int32Range `json:"delay"` + Rand int32 `json:"rand"` + Packet []byte `json:"packet"` +} + type HeaderCustomTCP struct { - Clients [][]uint8 `json:"clients"` - Servers [][]uint8 `json:"servers"` - Errors [][]uint8 `json:"errors"` + Clients [][]TCPItem `json:"clients"` + Servers [][]TCPItem `json:"servers"` + OnError []uint8 `json:"onError"` } func (c *HeaderCustomTCP) Build() (proto.Message, error) { for _, value := range c.Clients { - if len(value) > 8192 { - return nil, errors.New("len > 8192") + for _, item := range value { + if len(item.Packet) > 8192 { + return nil, errors.New("len > 8192") + } } } for _, value := range c.Servers { - if len(value) > 8192 { - return nil, errors.New("len > 8192") + for _, item := range value { + if len(item.Packet) > 8192 { + return nil, errors.New("len > 8192") + } } } - for _, value := range c.Errors { - if len(value) > 8192 { - return nil, errors.New("len > 8192") + if len(c.OnError) > 8192 { + return nil, errors.New("len > 8192") + } + + clients := make([]*custom.TCPSequence, len(c.Clients)) + for i, value := range c.Clients { + for _, item := range value { + clients[i].Items = append(clients[i].Items, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) + } + } + + servers := make([]*custom.TCPSequence, len(c.Servers)) + for i, value := range c.Servers { + for _, item := range value { + servers[i].Items = append(servers[i].Items, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) } } return &custom.TCPConfig{ - Clients: c.Clients, - Servers: c.Servers, - Errors: c.Errors, + Clients: clients, + Servers: servers, + OnError: c.OnError, }, nil } diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 853d084340cf..5e5ce3294f41 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -21,18 +21,130 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type TCPItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + DelayMin int64 `protobuf:"varint,1,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` + DelayMax int64 `protobuf:"varint,2,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` + Rand int32 `protobuf:"varint,3,opt,name=rand,proto3" json:"rand,omitempty"` + Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TCPItem) Reset() { + *x = TCPItem{} + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TCPItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TCPItem) ProtoMessage() {} + +func (x *TCPItem) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_custom_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 TCPItem.ProtoReflect.Descriptor instead. +func (*TCPItem) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{0} +} + +func (x *TCPItem) GetDelayMin() int64 { + if x != nil { + return x.DelayMin + } + return 0 +} + +func (x *TCPItem) GetDelayMax() int64 { + if x != nil { + return x.DelayMax + } + return 0 +} + +func (x *TCPItem) GetRand() int32 { + if x != nil { + return x.Rand + } + return 0 +} + +func (x *TCPItem) GetPacket() []byte { + if x != nil { + return x.Packet + } + return nil +} + +type TCPSequence struct { + state protoimpl.MessageState `protogen:"open.v1"` + Items []*TCPItem `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TCPSequence) Reset() { + *x = TCPSequence{} + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TCPSequence) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TCPSequence) ProtoMessage() {} + +func (x *TCPSequence) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + 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 TCPSequence.ProtoReflect.Descriptor instead. +func (*TCPSequence) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1} +} + +func (x *TCPSequence) GetItems() []*TCPItem { + if x != nil { + return x.Items + } + return nil +} + type TCPConfig struct { state protoimpl.MessageState `protogen:"open.v1"` - Clients [][]byte `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` - Servers [][]byte `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` - Errors [][]byte `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"` + Clients []*TCPSequence `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` + Servers []*TCPSequence `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` + OnError []byte `protobuf:"bytes,3,opt,name=on_error,json=onError,proto3" json:"on_error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TCPConfig) Reset() { *x = TCPConfig{} - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -44,7 +156,7 @@ func (x *TCPConfig) String() string { func (*TCPConfig) ProtoMessage() {} func (x *TCPConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[0] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -57,26 +169,26 @@ func (x *TCPConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use TCPConfig.ProtoReflect.Descriptor instead. func (*TCPConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{0} + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{2} } -func (x *TCPConfig) GetClients() [][]byte { +func (x *TCPConfig) GetClients() []*TCPSequence { if x != nil { return x.Clients } return nil } -func (x *TCPConfig) GetServers() [][]byte { +func (x *TCPConfig) GetServers() []*TCPSequence { if x != nil { return x.Servers } return nil } -func (x *TCPConfig) GetErrors() [][]byte { +func (x *TCPConfig) GetOnError() []byte { if x != nil { - return x.Errors + return x.OnError } return nil } @@ -91,7 +203,7 @@ type UDPConfig struct { func (x *UDPConfig) Reset() { *x = UDPConfig{} - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -103,7 +215,7 @@ func (x *UDPConfig) String() string { func (*UDPConfig) ProtoMessage() {} func (x *UDPConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[1] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -116,7 +228,7 @@ func (x *UDPConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use UDPConfig.ProtoReflect.Descriptor instead. func (*UDPConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1} + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{3} } func (x *UDPConfig) GetClient() []byte { @@ -137,11 +249,18 @@ var File_transport_internet_finalmask_header_custom_config_proto protoreflect.Fi const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" + "\n" + - "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"W\n" + - "\tTCPConfig\x12\x18\n" + - "\aclients\x18\x01 \x03(\fR\aclients\x12\x18\n" + - "\aservers\x18\x02 \x03(\fR\aservers\x12\x16\n" + - "\x06errors\x18\x03 \x03(\fR\x06errors\";\n" + + "7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"o\n" + + "\aTCPItem\x12\x1b\n" + + "\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" + + "\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" + + "\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" + + "\x06packet\x18\x04 \x01(\fR\x06packet\"]\n" + + "\vTCPSequence\x12N\n" + + "\x05items\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\x05items\"\xd6\x01\n" + + "\tTCPConfig\x12V\n" + + "\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" + + "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12\x19\n" + + "\bon_error\x18\x03 \x01(\fR\aonError\";\n" + "\tUDPConfig\x12\x16\n" + "\x06client\x18\x01 \x01(\fR\x06client\x12\x16\n" + "\x06server\x18\x02 \x01(\fR\x06serverB\xaf\x01\n" + @@ -159,17 +278,22 @@ func file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP() return file_transport_internet_finalmask_header_custom_config_proto_rawDescData } -var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any{ - (*TCPConfig)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPConfig - (*UDPConfig)(nil), // 1: xray.transport.internet.finalmask.header.custom.UDPConfig + (*TCPItem)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPItem + (*TCPSequence)(nil), // 1: xray.transport.internet.finalmask.header.custom.TCPSequence + (*TCPConfig)(nil), // 2: xray.transport.internet.finalmask.header.custom.TCPConfig + (*UDPConfig)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPConfig } var file_transport_internet_finalmask_header_custom_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 + 0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.items:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem + 1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence + 1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_transport_internet_finalmask_header_custom_config_proto_init() } @@ -183,7 +307,7 @@ func file_transport_internet_finalmask_header_custom_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index a0adaefacb64..582d75d3d455 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -6,10 +6,21 @@ option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/head option java_package = "com.xray.transport.internet.finalmask.header.custom"; option java_multiple_files = true; +message TCPItem { + int64 delay_min = 1; + int64 delay_max = 2; + int32 rand = 3; + bytes packet = 4; +} + +message TCPSequence { + repeated TCPItem items = 1; +} + message TCPConfig { - repeated bytes clients = 1; - repeated bytes servers = 2; - repeated bytes errors = 3; + repeated TCPSequence clients = 1; + repeated TCPSequence servers = 2; + bytes on_error = 3; } message UDPConfig { diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index babdb3617bdc..d2f4f81f9eab 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -2,16 +2,20 @@ package custom import ( "bytes" + "crypto/rand" "io" "net" "sync" + "time" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" ) type tcpCustomClient struct { - clients [][]byte - servers [][]byte + clients []*TCPSequence + servers []*TCPSequence } type tcpCustomClientConn struct { @@ -32,6 +36,22 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } + for _, sequence := range conn.header.clients { + for _, item := range sequence.Items { + if item.Rand > 0 { + item.Packet = make([]byte, 0, item.Rand) + } + } + } + + for _, sequence := range conn.header.servers { + for _, item := range sequence.Items { + if item.Rand > 0 { + item.Packet = make([]byte, 0, item.Rand) + } + } + } + conn.wg.Add(1) return conn, nil @@ -62,34 +82,54 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.clients { - n, err = c.Conn.Write(c.header.clients[i]) - if err != nil { - c.wg.Done() - return - } - if j < len(c.header.servers) { - n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) + for _, item := range c.header.clients[i].Items { + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + if item.Rand > 0 { + common.Must2(rand.Read(item.Packet)) + } + n, err = c.Conn.Write(item.Packet) if err != nil { c.wg.Done() return } - if !bytes.Equal(c.header.servers[j], buf[:n]) { - c.wg.Done() - return + } + if j < len(c.header.servers) { + for _, item := range c.header.servers[j].Items { + n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) + if err != nil { + c.wg.Done() + return + } + if item.Rand > 0 { + if n != len(item.Packet) { + c.wg.Done() + return + } + } else if !bytes.Equal(item.Packet, buf[:n]) { + c.wg.Done() + return + } } j++ } } for j < len(c.header.servers) { - n, err := io.ReadFull(c.Conn, buf[:len(c.header.servers[j])]) - if err != nil { - c.wg.Done() - return - } - if !bytes.Equal(c.header.servers[j], buf[:n]) { - c.wg.Done() - return + for _, item := range c.header.servers[j].Items { + n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) + if err != nil { + c.wg.Done() + return + } + if item.Rand > 0 { + if n != len(item.Packet) { + c.wg.Done() + return + } + } else if !bytes.Equal(item.Packet, buf[:n]) { + c.wg.Done() + return + } } j++ } @@ -108,9 +148,9 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } type tcpCustomServer struct { - clients [][]byte - servers [][]byte - serversError [][]byte + clients []*TCPSequence + servers []*TCPSequence + onError []byte } type tcpCustomServerConn struct { @@ -126,12 +166,28 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { conn := &tcpCustomServerConn{ Conn: raw, header: &tcpCustomServer{ - clients: c.Clients, - servers: c.Servers, - serversError: c.Errors, + clients: c.Clients, + servers: c.Servers, + onError: c.OnError, }, } + for _, sequence := range conn.header.clients { + for _, item := range sequence.Items { + if item.Rand > 0 { + item.Packet = make([]byte, 0, item.Rand) + } + } + } + + for _, sequence := range conn.header.servers { + for _, item := range sequence.Items { + if item.Rand > 0 { + item.Packet = make([]byte, 0, item.Rand) + } + } + } + conn.wg.Add(1) return conn, nil @@ -152,33 +208,52 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.clients { - n, err := io.ReadFull(c.Conn, buf[:len(c.header.clients[i])]) - if err != nil { - c.wg.Done() - return - } - if !bytes.Equal(c.header.clients[i], buf[:n]) { - if j < len(c.header.serversError) { - c.Conn.Write(c.header.serversError[j]) - } - c.wg.Done() - return - } - if j < len(c.header.servers) { - n, err = c.Conn.Write(c.header.servers[j]) + for _, item := range c.header.clients[i].Items { + n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) if err != nil { c.wg.Done() return } + if item.Rand > 0 { + if n != len(item.Packet) { + c.wg.Done() + return + } + } else if !bytes.Equal(item.Packet, buf[:n]) { + if len(c.header.onError) > 0 { + c.Conn.Write(c.header.onError) + } + c.wg.Done() + return + } + } + if j < len(c.header.servers) { + for _, item := range c.header.servers[i].Items { + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + if item.Rand > 0 { + common.Must2(rand.Read(item.Packet)) + } + n, err = c.Conn.Write(item.Packet) + if err != nil { + c.wg.Done() + return + } + } j++ } } for j < len(c.header.servers) { - n, err = c.Conn.Write(c.header.servers[j]) - if err != nil { - c.wg.Done() - return + for _, item := range c.header.servers[i].Items { + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + if item.Rand > 0 { + common.Must2(rand.Read(item.Packet)) + } + n, err = c.Conn.Write(item.Packet) + if err != nil { + c.wg.Done() + return + } } j++ } From 1041f62923777e6d428b70a8ed842f113bd925d3 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 11:19:14 +0800 Subject: [PATCH 13/75] 1 --- infra/conf/transport_internet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 3df7da01a598..a78106bef0a7 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1307,7 +1307,7 @@ type TCPItem struct { type HeaderCustomTCP struct { Clients [][]TCPItem `json:"clients"` Servers [][]TCPItem `json:"servers"` - OnError []uint8 `json:"onError"` + OnError []byte `json:"onError"` } func (c *HeaderCustomTCP) Build() (proto.Message, error) { From 9fd331784ed58f35b030dda1b37dfb6ebe73096f Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 11:58:29 +0800 Subject: [PATCH 14/75] 1 --- infra/conf/transport_internet.go | 4 +-- .../finalmask/header/custom/config.pb.go | 14 +++++----- .../finalmask/header/custom/config.proto | 2 +- .../internet/finalmask/header/custom/tcp.go | 28 +++++++++---------- transport/internet/finalmask/tcp_test.go | 20 ++++++++++--- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index a78106bef0a7..476fef3ca481 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1332,7 +1332,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { clients := make([]*custom.TCPSequence, len(c.Clients)) for i, value := range c.Clients { for _, item := range value { - clients[i].Items = append(clients[i].Items, &custom.TCPItem{ + clients[i].Sequence = append(clients[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), Rand: item.Rand, @@ -1344,7 +1344,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { servers := make([]*custom.TCPSequence, len(c.Servers)) for i, value := range c.Servers { for _, item := range value { - servers[i].Items = append(servers[i].Items, &custom.TCPItem{ + servers[i].Sequence = append(servers[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), Rand: item.Rand, diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 5e5ce3294f41..2d8c57d46c15 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -91,7 +91,7 @@ func (x *TCPItem) GetPacket() []byte { type TCPSequence struct { state protoimpl.MessageState `protogen:"open.v1"` - Items []*TCPItem `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + Sequence []*TCPItem `protobuf:"bytes,1,rep,name=sequence,proto3" json:"sequence,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -126,9 +126,9 @@ func (*TCPSequence) Descriptor() ([]byte, []int) { return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{1} } -func (x *TCPSequence) GetItems() []*TCPItem { +func (x *TCPSequence) GetSequence() []*TCPItem { if x != nil { - return x.Items + return x.Sequence } return nil } @@ -254,9 +254,9 @@ const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" "\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" + "\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" + "\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" + - "\x06packet\x18\x04 \x01(\fR\x06packet\"]\n" + - "\vTCPSequence\x12N\n" + - "\x05items\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\x05items\"\xd6\x01\n" + + "\x06packet\x18\x04 \x01(\fR\x06packet\"c\n" + + "\vTCPSequence\x12T\n" + + "\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\xd6\x01\n" + "\tTCPConfig\x12V\n" + "\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" + "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12\x19\n" + @@ -286,7 +286,7 @@ var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any (*UDPConfig)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPConfig } var file_transport_internet_finalmask_header_custom_config_proto_depIdxs = []int32{ - 0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.items:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem + 0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.sequence:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem 1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence 1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence 3, // [3:3] is the sub-list for method output_type diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index 582d75d3d455..28363feca9ef 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -14,7 +14,7 @@ message TCPItem { } message TCPSequence { - repeated TCPItem items = 1; + repeated TCPItem sequence = 1; } message TCPConfig { diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index d2f4f81f9eab..803797e3e390 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -36,16 +36,16 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for _, sequence := range conn.header.clients { - for _, item := range sequence.Items { + for _, client := range conn.header.clients { + for _, item := range client.Sequence { if item.Rand > 0 { item.Packet = make([]byte, 0, item.Rand) } } } - for _, sequence := range conn.header.servers { - for _, item := range sequence.Items { + for _, server := range conn.header.servers { + for _, item := range server.Sequence { if item.Rand > 0 { item.Packet = make([]byte, 0, item.Rand) } @@ -82,7 +82,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.clients { - for _, item := range c.header.clients[i].Items { + for _, item := range c.header.clients[i].Sequence { time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) if item.Rand > 0 { common.Must2(rand.Read(item.Packet)) @@ -94,7 +94,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } } if j < len(c.header.servers) { - for _, item := range c.header.servers[j].Items { + for _, item := range c.header.servers[j].Sequence { n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) if err != nil { c.wg.Done() @@ -115,7 +115,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { } for j < len(c.header.servers) { - for _, item := range c.header.servers[j].Items { + for _, item := range c.header.servers[j].Sequence { n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) if err != nil { c.wg.Done() @@ -172,16 +172,16 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for _, sequence := range conn.header.clients { - for _, item := range sequence.Items { + for _, client := range conn.header.clients { + for _, item := range client.Sequence { if item.Rand > 0 { item.Packet = make([]byte, 0, item.Rand) } } } - for _, sequence := range conn.header.servers { - for _, item := range sequence.Items { + for _, server := range conn.header.servers { + for _, item := range server.Sequence { if item.Rand > 0 { item.Packet = make([]byte, 0, item.Rand) } @@ -208,7 +208,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.clients { - for _, item := range c.header.clients[i].Items { + for _, item := range c.header.clients[i].Sequence { n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) if err != nil { c.wg.Done() @@ -228,7 +228,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { } } if j < len(c.header.servers) { - for _, item := range c.header.servers[i].Items { + for _, item := range c.header.servers[i].Sequence { time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) if item.Rand > 0 { common.Must2(rand.Read(item.Packet)) @@ -244,7 +244,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { } for j < len(c.header.servers) { - for _, item := range c.header.servers[i].Items { + for _, item := range c.header.servers[i].Sequence { time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) if item.Rand > 0 { common.Must2(rand.Read(item.Packet)) diff --git a/transport/internet/finalmask/tcp_test.go b/transport/internet/finalmask/tcp_test.go index d70974e9511f..c144c1c71387 100644 --- a/transport/internet/finalmask/tcp_test.go +++ b/transport/internet/finalmask/tcp_test.go @@ -51,11 +51,23 @@ func TestConnReadWrite(t *testing.T) { { name: "custom", mask: &custom.TCPConfig{ - Clients: [][]byte{ - {1}, + Clients: []*custom.TCPSequence{ + { + Sequence: []*custom.TCPItem{ + { + Packet: []byte{1}, + }, + }, + }, }, - Servers: [][]byte{ - {2}, + Servers: []*custom.TCPSequence{ + { + Sequence: []*custom.TCPItem{ + { + Packet: []byte{2}, + }, + }, + }, }, }, }, From 7b72f8e67f9053dfc069d0ae169802fbc6522156 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 13:29:49 +0800 Subject: [PATCH 15/75] udp custom header --- infra/conf/transport_internet.go | 47 +++-- .../finalmask/header/custom/config.pb.go | 96 ++++++++-- .../finalmask/header/custom/config.proto | 9 +- .../internet/finalmask/header/custom/tcp.go | 11 +- .../internet/finalmask/header/custom/udp.go | 164 +++++++++++------- transport/internet/finalmask/udp_test.go | 18 +- 6 files changed, 247 insertions(+), 98 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 476fef3ca481..c9541e3305de 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1283,13 +1283,13 @@ var ( }, "type", "settings") udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ + "header-custom": func() interface{} { return new(HeaderCustomUDP) }, "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) }, - "header-custom": func() interface{} { return new(HeaderCustomUDP) }, "mkcp-original": func() interface{} { return new(Original) }, "mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) }, "salamander": func() interface{} { return new(Salamander) }, @@ -1360,6 +1360,39 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { }, nil } +type UDPItem struct { + Rand int32 `json:"rand"` + Packet []byte `json:"packet"` +} + +type HeaderCustomUDP struct { + Client []UDPItem `json:"client"` + Server []UDPItem `json:"server"` +} + +func (c *HeaderCustomUDP) Build() (proto.Message, error) { + client := make([]*custom.UDPItem, len(c.Client)) + for _, item := range c.Client { + client = append(client, &custom.UDPItem{ + Rand: item.Rand, + Packet: item.Packet, + }) + } + + server := make([]*custom.UDPItem, len(c.Server)) + for _, item := range c.Server { + server = append(server, &custom.UDPItem{ + Rand: item.Rand, + Packet: item.Packet, + }) + } + + return &custom.UDPConfig{ + Client: client, + Server: server, + }, nil +} + type Dns struct { Domain string `json:"domain"` } @@ -1405,18 +1438,6 @@ func (c *Wireguard) Build() (proto.Message, error) { return &wireguard.Config{}, nil } -type HeaderCustomUDP struct { - Client []uint8 `json:"client"` - Server []uint8 `json:"server"` -} - -func (c *HeaderCustomUDP) Build() (proto.Message, error) { - return &custom.UDPConfig{ - Client: c.Client, - Server: c.Server, - }, nil -} - type Original struct{} func (c *Original) Build() (proto.Message, error) { diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 2d8c57d46c15..7c372e323c50 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -193,17 +193,69 @@ func (x *TCPConfig) GetOnError() []byte { return nil } +type UDPItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + Rand int32 `protobuf:"varint,1,opt,name=rand,proto3" json:"rand,omitempty"` + Packet []byte `protobuf:"bytes,2,opt,name=packet,proto3" json:"packet,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UDPItem) Reset() { + *x = UDPItem{} + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UDPItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UDPItem) ProtoMessage() {} + +func (x *UDPItem) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] + 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 UDPItem.ProtoReflect.Descriptor instead. +func (*UDPItem) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{3} +} + +func (x *UDPItem) GetRand() int32 { + if x != nil { + return x.Rand + } + return 0 +} + +func (x *UDPItem) GetPacket() []byte { + if x != nil { + return x.Packet + } + return nil +} + type UDPConfig struct { state protoimpl.MessageState `protogen:"open.v1"` - Client []byte `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` - Server []byte `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` + Client []*UDPItem `protobuf:"bytes,1,rep,name=client,proto3" json:"client,omitempty"` + Server []*UDPItem `protobuf:"bytes,2,rep,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UDPConfig) Reset() { *x = UDPConfig{} - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -215,7 +267,7 @@ func (x *UDPConfig) String() string { func (*UDPConfig) ProtoMessage() {} func (x *UDPConfig) ProtoReflect() protoreflect.Message { - mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[3] + mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -228,17 +280,17 @@ func (x *UDPConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use UDPConfig.ProtoReflect.Descriptor instead. func (*UDPConfig) Descriptor() ([]byte, []int) { - return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{3} + return file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP(), []int{4} } -func (x *UDPConfig) GetClient() []byte { +func (x *UDPConfig) GetClient() []*UDPItem { if x != nil { return x.Client } return nil } -func (x *UDPConfig) GetServer() []byte { +func (x *UDPConfig) GetServer() []*UDPItem { if x != nil { return x.Server } @@ -260,10 +312,13 @@ const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" "\tTCPConfig\x12V\n" + "\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" + "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12\x19\n" + - "\bon_error\x18\x03 \x01(\fR\aonError\";\n" + - "\tUDPConfig\x12\x16\n" + - "\x06client\x18\x01 \x01(\fR\x06client\x12\x16\n" + - "\x06server\x18\x02 \x01(\fR\x06serverB\xaf\x01\n" + + "\bon_error\x18\x03 \x01(\fR\aonError\"5\n" + + "\aUDPItem\x12\x12\n" + + "\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x16\n" + + "\x06packet\x18\x02 \x01(\fR\x06packet\"\xaf\x01\n" + + "\tUDPConfig\x12P\n" + + "\x06client\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06client\x12P\n" + + "\x06server\x18\x02 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06serverB\xaf\x01\n" + "3com.xray.transport.internet.finalmask.header.customP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/custom\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Customb\x06proto3" var ( @@ -278,22 +333,25 @@ func file_transport_internet_finalmask_header_custom_config_proto_rawDescGZIP() return file_transport_internet_finalmask_header_custom_config_proto_rawDescData } -var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_transport_internet_finalmask_header_custom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_transport_internet_finalmask_header_custom_config_proto_goTypes = []any{ (*TCPItem)(nil), // 0: xray.transport.internet.finalmask.header.custom.TCPItem (*TCPSequence)(nil), // 1: xray.transport.internet.finalmask.header.custom.TCPSequence (*TCPConfig)(nil), // 2: xray.transport.internet.finalmask.header.custom.TCPConfig - (*UDPConfig)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPConfig + (*UDPItem)(nil), // 3: xray.transport.internet.finalmask.header.custom.UDPItem + (*UDPConfig)(nil), // 4: xray.transport.internet.finalmask.header.custom.UDPConfig } var file_transport_internet_finalmask_header_custom_config_proto_depIdxs = []int32{ 0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.sequence:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem 1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence 1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 3, // 3: xray.transport.internet.finalmask.header.custom.UDPConfig.client:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem + 3, // 4: xray.transport.internet.finalmask.header.custom.UDPConfig.server:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_transport_internet_finalmask_header_custom_config_proto_init() } @@ -307,7 +365,7 @@ func file_transport_internet_finalmask_header_custom_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_custom_config_proto_rawDesc), len(file_transport_internet_finalmask_header_custom_config_proto_rawDesc)), NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index 28363feca9ef..2624157d46c4 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -23,7 +23,12 @@ message TCPConfig { bytes on_error = 3; } +message UDPItem { + int32 rand = 1; + bytes packet = 2; +} + message UDPConfig { - bytes client = 1; - bytes server = 2; + repeated UDPItem client = 1; + repeated UDPItem server = 2; } \ No newline at end of file diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 803797e3e390..ebff7ce17b69 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -39,7 +39,7 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { for _, client := range conn.header.clients { for _, item := range client.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, 0, item.Rand) + item.Packet = make([]byte, item.Rand) } } } @@ -47,7 +47,7 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { for _, server := range conn.header.servers { for _, item := range server.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, 0, item.Rand) + item.Packet = make([]byte, item.Rand) } } } @@ -175,7 +175,7 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { for _, client := range conn.header.clients { for _, item := range client.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, 0, item.Rand) + item.Packet = make([]byte, item.Rand) } } } @@ -183,7 +183,7 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { for _, server := range conn.header.servers { for _, item := range server.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, 0, item.Rand) + item.Packet = make([]byte, item.Rand) } } } @@ -216,6 +216,9 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { } if item.Rand > 0 { if n != len(item.Packet) { + if len(c.header.onError) > 0 { + c.Conn.Write(c.header.onError) + } c.wg.Done() return } diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 2fa5461c9611..a735c372a38b 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -2,23 +2,32 @@ package custom import ( "bytes" + "crypto/rand" "io" "net" sync "sync" + "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" ) type udpCustomClient struct { - client []byte - server []byte - - clientSize int32 - serverSize int32 + client []*UDPItem + server []*UDPItem + merged []byte } func (h *udpCustomClient) Serialize(b []byte) { - copy(b, h.client) + index := 0 + for _, item := range h.client { + if item.Rand > 0 { + common.Must2(rand.Read(h.merged[index : index+int(item.Rand)])) + index += int(item.Rand) + } else { + index += len(item.Packet) + } + } + copy(b, h.merged) } type udpCustomClientConn struct { @@ -43,9 +52,6 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in header: &udpCustomClient{ client: c.Client, server: c.Server, - - clientSize: int32(len(c.Client)), - serverSize: int32(len(c.Server)), }, } @@ -54,11 +60,22 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in conn.writeBuf = make([]byte, 8192) } + index := 0 + for _, item := range conn.header.client { + if item.Rand > 0 { + conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...) + index += int(item.Rand) + } else { + conn.header.merged = append(conn.header.merged, item.Packet...) + index += len(item.Packet) + } + } + return conn, nil } func (c *udpCustomClientConn) Size() int32 { - return c.header.clientSize + return int32(len(c.header.merged)) } func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { @@ -71,25 +88,29 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return n, addr, err } - if n < int(c.header.serverSize) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + index := 0 + for _, item := range c.header.server { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + c.readMutex.Unlock() + return 0, addr, errors.New("header mismatch") + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + c.readMutex.Unlock() + return 0, addr, errors.New("header mismatch") + } + index += length } - if len(p) < n-int(c.header.serverSize) { + if len(p) < n-index { c.readMutex.Unlock() return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if !bytes.Equal(c.header.server, c.readBuf[:c.header.serverSize]) { - c.readMutex.Unlock() - return 0, addr, errors.New("header mismatch") - } - - copy(p, c.readBuf[c.header.serverSize:n]) + copy(p, c.readBuf[index:n]) c.readMutex.Unlock() - return n - int(c.header.serverSize), addr, err + return n - index, addr, err } n, addr, err = c.PacketConn.ReadFrom(p) @@ -97,17 +118,21 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return n, addr, err } - if n < int(c.header.serverSize) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } - - if !bytes.Equal(c.header.server, p[:c.header.serverSize]) { - return 0, addr, errors.New("header mismatch") + index := 0 + for _, item := range c.header.server { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + return 0, addr, errors.New("header mismatch") + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, p[index:index+length]) { + return 0, addr, errors.New("header mismatch") + } + index += length } - copy(p, p[c.header.serverSize:n]) + copy(p, p[index:n]) - return n - int(c.header.serverSize), addr, err + return n - index, addr, err } func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -145,15 +170,22 @@ func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error } type udpCustomServer struct { - client []byte - server []byte - - clientSize int32 - serverSize int32 + client []*UDPItem + server []*UDPItem + merged []byte } func (h *udpCustomServer) Serialize(b []byte) { - copy(b, h.server) + index := 0 + for _, item := range h.server { + if item.Rand > 0 { + common.Must2(rand.Read(h.merged[index : index+int(item.Rand)])) + index += int(item.Rand) + } else { + index += len(item.Packet) + } + } + copy(b, h.merged) } type udpCustomServerConn struct { @@ -178,9 +210,6 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in header: &udpCustomServer{ client: c.Client, server: c.Server, - - clientSize: int32(len(c.Client)), - serverSize: int32(len(c.Server)), }, } @@ -189,11 +218,22 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in conn.writeBuf = make([]byte, 8192) } + index := 0 + for _, item := range conn.header.server { + if item.Rand > 0 { + conn.header.merged = append(conn.header.merged, make([]byte, item.Rand)...) + index += int(item.Rand) + } else { + conn.header.merged = append(conn.header.merged, item.Packet...) + index += len(item.Packet) + } + } + return conn, nil } func (c *udpCustomServerConn) Size() int32 { - return c.header.serverSize + return int32(len(c.header.merged)) } func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { @@ -206,25 +246,29 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return n, addr, err } - if n < int(c.header.clientSize) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + index := 0 + for _, item := range c.header.client { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + c.readMutex.Unlock() + return 0, addr, errors.New("header mismatch") + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + c.readMutex.Unlock() + return 0, addr, errors.New("header mismatch") + } + index += length } - if len(p) < n-int(c.header.clientSize) { + if len(p) < n-index { c.readMutex.Unlock() return 0, addr, errors.New("header").Base(io.ErrShortBuffer) } - if !bytes.Equal(c.header.client, c.readBuf[:c.header.clientSize]) { - c.readMutex.Unlock() - return 0, addr, errors.New("checksum mismatch") - } - - copy(p, c.readBuf[c.header.clientSize:n]) + copy(p, c.readBuf[index:n]) c.readMutex.Unlock() - return n - int(c.header.clientSize), addr, err + return n - index, addr, err } n, addr, err = c.PacketConn.ReadFrom(p) @@ -232,17 +276,21 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro return n, addr, err } - if n < int(c.header.clientSize) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } - - if !bytes.Equal(c.header.client, p[:c.header.clientSize]) { - return 0, addr, errors.New("checksum mismatch") + index := 0 + for _, item := range c.header.client { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + return 0, addr, errors.New("header mismatch") + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, p[index:index+length]) { + return 0, addr, errors.New("header mismatch") + } + index += length } - copy(p, p[c.header.clientSize:n]) + copy(p, p[index:n]) - return n - int(c.header.clientSize), addr, err + return n - index, addr, err } func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { diff --git a/transport/internet/finalmask/udp_test.go b/transport/internet/finalmask/udp_test.go index 02b6b1cb111b..49cdd9233c3c 100644 --- a/transport/internet/finalmask/udp_test.go +++ b/transport/internet/finalmask/udp_test.go @@ -86,8 +86,22 @@ func TestPacketConnReadWrite(t *testing.T) { { name: "custom", mask: &custom.UDPConfig{ - Client: []byte{1, 1, 1}, - Server: []byte{}, + Client: []*custom.UDPItem{ + { + Packet: []byte{1}, + }, + { + Rand: 1, + }, + }, + Server: []*custom.UDPItem{ + { + Packet: []byte{1}, + }, + { + Rand: 1, + }, + }, }, }, { From caf41526d99e0d10692c0aab9910c5b2db1c8476 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 14:26:07 +0800 Subject: [PATCH 16/75] noise mask --- infra/conf/freedom.go | 143 ++++++----- infra/conf/transport_internet.go | 39 ++- transport/internet/finalmask/noise/config.go | 14 ++ .../internet/finalmask/noise/config.pb.go | 225 ++++++++++++++++++ .../internet/finalmask/noise/config.proto | 21 ++ transport/internet/finalmask/noise/conn.go | 96 ++++++++ 6 files changed, 462 insertions(+), 76 deletions(-) create mode 100644 transport/internet/finalmask/noise/config.go create mode 100644 transport/internet/finalmask/noise/config.pb.go create mode 100644 transport/internet/finalmask/noise/config.proto create mode 100644 transport/internet/finalmask/noise/conn.go diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 65930a2e8a29..810e0346fea1 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -1,8 +1,6 @@ package conf import ( - "encoding/base64" - "encoding/hex" "net" "strings" @@ -10,8 +8,8 @@ import ( v2net "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/proxy/freedom" - "google.golang.org/protobuf/proto" "github.com/xtls/xray-core/transport/internet" + "google.golang.org/protobuf/proto" ) type FreedomConfig struct { @@ -32,12 +30,12 @@ type Fragment struct { MaxSplit *Int32Range `json:"maxSplit"` } -type Noise struct { - Type string `json:"type"` - Packet string `json:"packet"` - Delay *Int32Range `json:"delay"` - ApplyTo string `json:"applyTo"` -} +// type Noise struct { +// Type string `json:"type"` +// Packet string `json:"packet"` +// Delay *Int32Range `json:"delay"` +// ApplyTo string `json:"applyTo"` +// } // Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { @@ -126,17 +124,18 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } if c.Noise != nil { - return nil, errors.PrintRemovedFeatureError("noise = { ... }", "noises = [ { ... } ]") + return nil, errors.PrintRemovedFeatureError("noise", "finalmask/udp noise") } if c.Noises != nil { - for _, n := range c.Noises { - NConfig, err := ParseNoise(n) - if err != nil { - return nil, err - } - config.Noises = append(config.Noises, NConfig) - } + // for _, n := range c.Noises { + // NConfig, err := ParseNoise(n) + // if err != nil { + // return nil, err + // } + // config.Noises = append(config.Noises, NConfig) + // } + return nil, errors.PrintRemovedFeatureError("noise", "finalmask/udp noise") } config.UserLevel = c.UserLevel @@ -165,58 +164,58 @@ func (c *FreedomConfig) Build() (proto.Message, error) { return config, nil } -func ParseNoise(noise *Noise) (*freedom.Noise, error) { - var err error - NConfig := new(freedom.Noise) - noise.Packet = strings.TrimSpace(noise.Packet) - - switch noise.Type { - case "rand": - min, max, err := ParseRangeString(noise.Packet) - if err != nil { - return nil, errors.New("invalid value for rand Length").Base(err) - } - NConfig.LengthMin = uint64(min) - NConfig.LengthMax = uint64(max) - if NConfig.LengthMin == 0 { - return nil, errors.New("rand lengthMin or lengthMax cannot be 0") - } - - case "str": - // user input string - NConfig.Packet = []byte(noise.Packet) - - case "hex": - // user input hex - NConfig.Packet, err = hex.DecodeString(noise.Packet) - if err != nil { - return nil, errors.New("Invalid hex string").Base(err) - } - - case "base64": - // user input base64 - NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet)) - if err != nil { - return nil, errors.New("Invalid base64 string").Base(err) - } - - default: - return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported") - } - - if noise.Delay != nil { - NConfig.DelayMin = uint64(noise.Delay.From) - NConfig.DelayMax = uint64(noise.Delay.To) - } - switch strings.ToLower(noise.ApplyTo) { - case "", "ip", "all": - NConfig.ApplyTo = "ip" - case "ipv4": - NConfig.ApplyTo = "ipv4" - case "ipv6": - NConfig.ApplyTo = "ipv6" - default: - return nil, errors.New("Invalid applyTo, only ip/ipv4/ipv6 are supported") - } - return NConfig, nil -} +// func ParseNoise(noise *Noise) (*freedom.Noise, error) { +// var err error +// NConfig := new(freedom.Noise) +// noise.Packet = strings.TrimSpace(noise.Packet) + +// switch noise.Type { +// case "rand": +// min, max, err := ParseRangeString(noise.Packet) +// if err != nil { +// return nil, errors.New("invalid value for rand Length").Base(err) +// } +// NConfig.LengthMin = uint64(min) +// NConfig.LengthMax = uint64(max) +// if NConfig.LengthMin == 0 { +// return nil, errors.New("rand lengthMin or lengthMax cannot be 0") +// } + +// case "str": +// // user input string +// NConfig.Packet = []byte(noise.Packet) + +// case "hex": +// // user input hex +// NConfig.Packet, err = hex.DecodeString(noise.Packet) +// if err != nil { +// return nil, errors.New("Invalid hex string").Base(err) +// } + +// case "base64": +// // user input base64 +// NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet)) +// if err != nil { +// return nil, errors.New("Invalid base64 string").Base(err) +// } + +// default: +// return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported") +// } + +// if noise.Delay != nil { +// NConfig.DelayMin = uint64(noise.Delay.From) +// NConfig.DelayMax = uint64(noise.Delay.To) +// } +// switch strings.ToLower(noise.ApplyTo) { +// case "", "ip", "all": +// NConfig.ApplyTo = "ip" +// case "ipv4": +// NConfig.ApplyTo = "ipv4" +// case "ipv6": +// NConfig.ApplyTo = "ipv6" +// default: +// return nil, errors.New("Invalid applyTo, only ip/ipv4/ipv6 are supported") +// } +// return NConfig, nil +// } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index c9541e3305de..7945d0bfe293 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -27,6 +27,7 @@ import ( "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/noise" "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/finalmask/xicmp" @@ -1313,14 +1314,14 @@ type HeaderCustomTCP struct { func (c *HeaderCustomTCP) Build() (proto.Message, error) { for _, value := range c.Clients { for _, item := range value { - if len(item.Packet) > 8192 { + if len(item.Packet) > 8192 || item.Rand > 8192 { return nil, errors.New("len > 8192") } } } for _, value := range c.Servers { for _, item := range value { - if len(item.Packet) > 8192 { + if len(item.Packet) > 8192 || item.Rand > 8192 { return nil, errors.New("len > 8192") } } @@ -1360,6 +1361,36 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { }, nil } +type NoiseItem struct { + Rand Int32Range `json:"rand"` + Packet []byte `json:"packet"` + Delay Int32Range `json:"delay"` +} + +type Noise struct { + Reset Int32Range `json:"reset"` + Noise []NoiseItem `json:"noise"` +} + +func (c *Noise) Build() (proto.Message, error) { + noiseSlice := make([]*noise.Item, 0, len(c.Noise)) + for _, item := range c.Noise { + noiseSlice = append(noiseSlice, &noise.Item{ + RandMin: int64(item.Rand.From), + RandMax: int64(item.Rand.To), + Packet: item.Packet, + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + }) + } + + return &noise.Config{ + ResetMin: int64(c.Reset.From), + ResetMax: int64(c.Reset.To), + Items: noiseSlice, + }, nil +} + type UDPItem struct { Rand int32 `json:"rand"` Packet []byte `json:"packet"` @@ -1371,7 +1402,7 @@ type HeaderCustomUDP struct { } func (c *HeaderCustomUDP) Build() (proto.Message, error) { - client := make([]*custom.UDPItem, len(c.Client)) + client := make([]*custom.UDPItem, 0, len(c.Client)) for _, item := range c.Client { client = append(client, &custom.UDPItem{ Rand: item.Rand, @@ -1379,7 +1410,7 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) { }) } - server := make([]*custom.UDPItem, len(c.Server)) + server := make([]*custom.UDPItem, 0, len(c.Server)) for _, item := range c.Server { server = append(server, &custom.UDPItem{ Rand: item.Rand, diff --git a/transport/internet/finalmask/noise/config.go b/transport/internet/finalmask/noise/config.go new file mode 100644 index 000000000000..b3dabd058808 --- /dev/null +++ b/transport/internet/finalmask/noise/config.go @@ -0,0 +1,14 @@ +package noise + +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/noise/config.pb.go b/transport/internet/finalmask/noise/config.pb.go new file mode 100644 index 000000000000..16e1b25328fd --- /dev/null +++ b/transport/internet/finalmask/noise/config.pb.go @@ -0,0 +1,225 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.33.5 +// source: transport/internet/finalmask/noise/config.proto + +package noise + +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 Item struct { + state protoimpl.MessageState `protogen:"open.v1"` + RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"` + RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"` + Packet []byte `protobuf:"bytes,3,opt,name=packet,proto3" json:"packet,omitempty"` + DelayMin int64 `protobuf:"varint,4,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` + DelayMax int64 `protobuf:"varint,5,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Item) Reset() { + *x = Item{} + mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Item) ProtoMessage() {} + +func (x *Item) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_finalmask_noise_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 Item.ProtoReflect.Descriptor instead. +func (*Item) Descriptor() ([]byte, []int) { + return file_transport_internet_finalmask_noise_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Item) GetRandMin() int64 { + if x != nil { + return x.RandMin + } + return 0 +} + +func (x *Item) GetRandMax() int64 { + if x != nil { + return x.RandMax + } + return 0 +} + +func (x *Item) GetPacket() []byte { + if x != nil { + return x.Packet + } + return nil +} + +func (x *Item) GetDelayMin() int64 { + if x != nil { + return x.DelayMin + } + return 0 +} + +func (x *Item) GetDelayMax() int64 { + if x != nil { + return x.DelayMax + } + return 0 +} + +type Config struct { + state protoimpl.MessageState `protogen:"open.v1"` + ResetMin int64 `protobuf:"varint,1,opt,name=reset_min,json=resetMin,proto3" json:"reset_min,omitempty"` + ResetMax int64 `protobuf:"varint,2,opt,name=reset_max,json=resetMax,proto3" json:"reset_max,omitempty"` + Items []*Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_noise_config_proto_msgTypes[1] + 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_noise_config_proto_msgTypes[1] + 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_noise_config_proto_rawDescGZIP(), []int{1} +} + +func (x *Config) GetResetMin() int64 { + if x != nil { + return x.ResetMin + } + return 0 +} + +func (x *Config) GetResetMax() int64 { + if x != nil { + return x.ResetMax + } + return 0 +} + +func (x *Config) GetItems() []*Item { + if x != nil { + return x.Items + } + return nil +} + +var File_transport_internet_finalmask_noise_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" + + "\n" + + "/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\x8e\x01\n" + + "\x04Item\x12\x19\n" + + "\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" + + "\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" + + "\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" + + "\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" + + "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" + + "\x06Config\x12\x1b\n" + + "\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" + + "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" + + "\x05items\x18\x03 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" + + "+com.xray.transport.internet.finalmask.noiseP\x01Z xray.transport.internet.finalmask.noise.Item + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_transport_internet_finalmask_noise_config_proto_init() } +func file_transport_internet_finalmask_noise_config_proto_init() { + if File_transport_internet_finalmask_noise_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_noise_config_proto_rawDesc), len(file_transport_internet_finalmask_noise_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_noise_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_noise_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_noise_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_noise_config_proto = out.File + file_transport_internet_finalmask_noise_config_proto_goTypes = nil + file_transport_internet_finalmask_noise_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/noise/config.proto b/transport/internet/finalmask/noise/config.proto new file mode 100644 index 000000000000..552603faf2ff --- /dev/null +++ b/transport/internet/finalmask/noise/config.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.noise; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Noise"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/noise"; +option java_package = "com.xray.transport.internet.finalmask.noise"; +option java_multiple_files = true; + +message Item { + int64 rand_min = 1; + int64 rand_max = 2; + bytes packet = 3; + int64 delay_min = 4; + int64 delay_max = 5; +} + +message Config { + int64 reset_min = 1; + int64 reset_max = 2; + repeated Item items = 3; +} diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go new file mode 100644 index 000000000000..4c4dc7827f31 --- /dev/null +++ b/transport/internet/finalmask/noise/conn.go @@ -0,0 +1,96 @@ +package noise + +import ( + "crypto/rand" + "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 noiseConn struct { + net.PacketConn + config *Config + m map[string]struct{} + stop chan struct{} + once sync.Once + mutex sync.RWMutex +} + +func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { + if !end { + return nil, errors.New("noise requires being at the outermost level") + } + + conn := &noiseConn{ + PacketConn: raw, + config: c, + m: make(map[string]struct{}), + stop: make(chan struct{}), + } + + if conn.config.ResetMax > 0 { + go conn.reset() + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { + return NewConnClient(c, raw, end) +} + +func (c *noiseConn) reset() { + ticker := time.NewTicker(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + c.mutex.Lock() + c.m = make(map[string]struct{}) + c.mutex.Unlock() + ticker.Reset(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second) + case <-c.stop: + return + } + } +} + +func (c *noiseConn) Size() int32 { + return 0 +} + +func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.mutex.RLock() + _, ready := c.m[addr.String()] + c.mutex.RUnlock() + + if !ready { + c.mutex.Lock() + _, ready = c.m[addr.String()] + if !ready { + for _, item := range c.config.Items { + if item.RandMax > 0 { + item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax)) + common.Must2(rand.Read(item.Packet)) + } + c.PacketConn.WriteTo(item.Packet, addr) + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + c.m[addr.String()] = struct{}{} + } + c.mutex.Unlock() + } + + return c.PacketConn.WriteTo(p, addr) +} + +func (c *noiseConn) Close() error { + c.once.Do(func() { + close(c.stop) + }) + return c.PacketConn.Close() +} From 91a18a1142f4c19d4f407ab45d0d1e2dde32f602 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 15:29:31 +0800 Subject: [PATCH 17/75] fragment mask --- .../internet/finalmask/fragment/config.go | 14 ++ .../internet/finalmask/fragment/config.pb.go | 189 ++++++++++++++++++ .../internet/finalmask/fragment/config.proto | 18 ++ transport/internet/finalmask/fragment/conn.go | 114 +++++++++++ 4 files changed, 335 insertions(+) create mode 100644 transport/internet/finalmask/fragment/config.go create mode 100644 transport/internet/finalmask/fragment/config.pb.go create mode 100644 transport/internet/finalmask/fragment/config.proto create mode 100644 transport/internet/finalmask/fragment/conn.go diff --git a/transport/internet/finalmask/fragment/config.go b/transport/internet/finalmask/fragment/config.go new file mode 100644 index 000000000000..df34d08cd90a --- /dev/null +++ b/transport/internet/finalmask/fragment/config.go @@ -0,0 +1,14 @@ +package fragment + +import "net" + +func (c *Config) TCP() { +} + +func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) { + return NewConnClient(c, raw) +} + +func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) { + return NewConnServer(c, raw) +} diff --git a/transport/internet/finalmask/fragment/config.pb.go b/transport/internet/finalmask/fragment/config.pb.go new file mode 100644 index 000000000000..c8660f5ec34c --- /dev/null +++ b/transport/internet/finalmask/fragment/config.pb.go @@ -0,0 +1,189 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.33.5 +// source: transport/internet/finalmask/fragment/config.proto + +package fragment + +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"` + PacketsFrom int64 `protobuf:"varint,1,opt,name=packets_from,json=packetsFrom,proto3" json:"packets_from,omitempty"` + PacketsTo int64 `protobuf:"varint,2,opt,name=packets_to,json=packetsTo,proto3" json:"packets_to,omitempty"` + LengthMin int64 `protobuf:"varint,3,opt,name=length_min,json=lengthMin,proto3" json:"length_min,omitempty"` + LengthMax int64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"` + DelayMin int64 `protobuf:"varint,5,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` + DelayMax int64 `protobuf:"varint,6,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` + MaxSplitMin int64 `protobuf:"varint,7,opt,name=max_split_min,json=maxSplitMin,proto3" json:"max_split_min,omitempty"` + MaxSplitMax int64 `protobuf:"varint,8,opt,name=max_split_max,json=maxSplitMax,proto3" json:"max_split_max,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Config) Reset() { + *x = Config{} + mi := &file_transport_internet_finalmask_fragment_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_fragment_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_fragment_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetPacketsFrom() int64 { + if x != nil { + return x.PacketsFrom + } + return 0 +} + +func (x *Config) GetPacketsTo() int64 { + if x != nil { + return x.PacketsTo + } + return 0 +} + +func (x *Config) GetLengthMin() int64 { + if x != nil { + return x.LengthMin + } + return 0 +} + +func (x *Config) GetLengthMax() int64 { + if x != nil { + return x.LengthMax + } + return 0 +} + +func (x *Config) GetDelayMin() int64 { + if x != nil { + return x.DelayMin + } + return 0 +} + +func (x *Config) GetDelayMax() int64 { + if x != nil { + return x.DelayMax + } + return 0 +} + +func (x *Config) GetMaxSplitMin() int64 { + if x != nil { + return x.MaxSplitMin + } + return 0 +} + +func (x *Config) GetMaxSplitMax() int64 { + if x != nil { + return x.MaxSplitMax + } + return 0 +} + +var File_transport_internet_finalmask_fragment_config_proto protoreflect.FileDescriptor + +const file_transport_internet_finalmask_fragment_config_proto_rawDesc = "" + + "\n" + + "2transport/internet/finalmask/fragment/config.proto\x12*xray.transport.internet.finalmask.fragment\"\x8a\x02\n" + + "\x06Config\x12!\n" + + "\fpackets_from\x18\x01 \x01(\x03R\vpacketsFrom\x12\x1d\n" + + "\n" + + "packets_to\x18\x02 \x01(\x03R\tpacketsTo\x12\x1d\n" + + "\n" + + "length_min\x18\x03 \x01(\x03R\tlengthMin\x12\x1d\n" + + "\n" + + "length_max\x18\x04 \x01(\x03R\tlengthMax\x12\x1b\n" + + "\tdelay_min\x18\x05 \x01(\x03R\bdelayMin\x12\x1b\n" + + "\tdelay_max\x18\x06 \x01(\x03R\bdelayMax\x12\"\n" + + "\rmax_split_min\x18\a \x01(\x03R\vmaxSplitMin\x12\"\n" + + "\rmax_split_max\x18\b \x01(\x03R\vmaxSplitMaxB\xa0\x01\n" + + ".com.xray.transport.internet.finalmask.fragmentP\x01Z?github.com/xtls/xray-core/transport/internet/finalmask/fragment\xaa\x02*Xray.Transport.Internet.Finalmask.Fragmentb\x06proto3" + +var ( + file_transport_internet_finalmask_fragment_config_proto_rawDescOnce sync.Once + file_transport_internet_finalmask_fragment_config_proto_rawDescData []byte +) + +func file_transport_internet_finalmask_fragment_config_proto_rawDescGZIP() []byte { + file_transport_internet_finalmask_fragment_config_proto_rawDescOnce.Do(func() { + file_transport_internet_finalmask_fragment_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc))) + }) + return file_transport_internet_finalmask_fragment_config_proto_rawDescData +} + +var file_transport_internet_finalmask_fragment_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_finalmask_fragment_config_proto_goTypes = []any{ + (*Config)(nil), // 0: xray.transport.internet.finalmask.fragment.Config +} +var file_transport_internet_finalmask_fragment_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_fragment_config_proto_init() } +func file_transport_internet_finalmask_fragment_config_proto_init() { + if File_transport_internet_finalmask_fragment_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_fragment_config_proto_rawDesc), len(file_transport_internet_finalmask_fragment_config_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_finalmask_fragment_config_proto_goTypes, + DependencyIndexes: file_transport_internet_finalmask_fragment_config_proto_depIdxs, + MessageInfos: file_transport_internet_finalmask_fragment_config_proto_msgTypes, + }.Build() + File_transport_internet_finalmask_fragment_config_proto = out.File + file_transport_internet_finalmask_fragment_config_proto_goTypes = nil + file_transport_internet_finalmask_fragment_config_proto_depIdxs = nil +} diff --git a/transport/internet/finalmask/fragment/config.proto b/transport/internet/finalmask/fragment/config.proto new file mode 100644 index 000000000000..62aaf18640a9 --- /dev/null +++ b/transport/internet/finalmask/fragment/config.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package xray.transport.internet.finalmask.fragment; +option csharp_namespace = "Xray.Transport.Internet.Finalmask.Fragment"; +option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/fragment"; +option java_package = "com.xray.transport.internet.finalmask.fragment"; +option java_multiple_files = true; + +message Config { + int64 packets_from = 1; + int64 packets_to = 2; + int64 length_min = 3; + int64 length_max = 4; + int64 delay_min = 5; + int64 delay_max = 6; + int64 max_split_min = 7; + int64 max_split_max = 8; +} \ No newline at end of file diff --git a/transport/internet/finalmask/fragment/conn.go b/transport/internet/finalmask/fragment/conn.go new file mode 100644 index 000000000000..8d48adc628d9 --- /dev/null +++ b/transport/internet/finalmask/fragment/conn.go @@ -0,0 +1,114 @@ +package fragment + +import ( + "net" + "time" + + "github.com/xtls/xray-core/common/crypto" +) + +type fragmentConn struct { + net.Conn + config *Config + count uint64 +} + +func NewConnClient(c *Config, raw net.Conn) (net.Conn, error) { + conn := &fragmentConn{ + Conn: raw, + config: c, + } + + return conn, nil +} + +func NewConnServer(c *Config, raw net.Conn) (net.Conn, error) { + return NewConnClient(c, raw) +} + +func (c *fragmentConn) TcpMaskConn() {} + +func (c *fragmentConn) RawConn() net.Conn { + return c.Conn +} + +func (c *fragmentConn) Write(p []byte) (n int, err error) { + c.count++ + + if c.config.PacketsFrom == 0 && c.config.PacketsTo == 1 { + if c.count != 1 || len(p) <= 5 || p[0] != 22 { + return c.Conn.Write(p) + } + recordLen := 5 + ((int(p[3]) << 8) | int(p[4])) + if len(p) < recordLen { + return c.Conn.Write(p) + } + data := p[5:recordLen] + buff := make([]byte, 2048) + var hello []byte + maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax) + var splitNum int64 + for from := 0; ; { + to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax)) + splitNum++ + if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) { + to = len(data) + } + l := to - from + if 5+l > len(buff) { + buff = make([]byte, 5+l) + } + copy(buff[:3], p) + copy(buff[5:], data[from:to]) + from = to + buff[3] = byte(l >> 8) + buff[4] = byte(l) + if c.config.DelayMax == 0 { + hello = append(hello, buff[:5+l]...) + } else { + _, err := c.Conn.Write(buff[:5+l]) + time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond) + if err != nil { + return 0, err + } + } + if from == len(data) { + if len(hello) > 0 { + _, err := c.Conn.Write(hello) + if err != nil { + return 0, err + } + } + if len(p) > recordLen { + n, err := c.Conn.Write(p[recordLen:]) + if err != nil { + return recordLen + n, err + } + } + return len(p), nil + } + } + } + + if c.config.PacketsFrom != 0 && (c.count < uint64(c.config.PacketsFrom) || c.count > uint64(c.config.PacketsTo)) { + return c.Conn.Write(p) + } + maxSplit := crypto.RandBetween(c.config.MaxSplitMin, c.config.MaxSplitMax) + var splitNum int64 + for from := 0; ; { + to := from + int(crypto.RandBetween(c.config.LengthMin, c.config.LengthMax)) + splitNum++ + if to > len(p) || (maxSplit > 0 && splitNum >= maxSplit) { + to = len(p) + } + n, err := c.Conn.Write(p[from:to]) + from += n + if err != nil { + return from, err + } + time.Sleep(time.Duration(crypto.RandBetween(c.config.DelayMin, c.config.DelayMax)) * time.Millisecond) + if from >= len(p) { + return from, nil + } + } +} From 7942479f5a4ee9459aed160260a75a028fcca749 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 15:42:55 +0800 Subject: [PATCH 18/75] fragment --- infra/conf/freedom.go | 128 +-------------------- infra/conf/transport_internet.go | 45 ++++++++ proxy/freedom/freedom.go | 183 +------------------------------ 3 files changed, 47 insertions(+), 309 deletions(-) diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 810e0346fea1..62f2b306d16a 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -23,21 +23,6 @@ type FreedomConfig struct { ProxyProtocol uint32 `json:"proxyProtocol"` } -type Fragment struct { - Packets string `json:"packets"` - Length *Int32Range `json:"length"` - Interval *Int32Range `json:"interval"` - MaxSplit *Int32Range `json:"maxSplit"` -} - -// type Noise struct { -// Type string `json:"type"` -// Packet string `json:"packet"` -// Delay *Int32Range `json:"delay"` -// ApplyTo string `json:"applyTo"` -// } - -// Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) targetStrategy := c.TargetStrategy @@ -72,55 +57,7 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } if c.Fragment != nil { - config.Fragment = new(freedom.Fragment) - - switch strings.ToLower(c.Fragment.Packets) { - case "tlshello": - // TLS Hello Fragmentation (into multiple handshake messages) - config.Fragment.PacketsFrom = 0 - config.Fragment.PacketsTo = 1 - case "": - // TCP Segmentation (all packets) - config.Fragment.PacketsFrom = 0 - config.Fragment.PacketsTo = 0 - default: - // TCP Segmentation (range) - from, to, err := ParseRangeString(c.Fragment.Packets) - if err != nil { - return nil, errors.New("Invalid PacketsFrom").Base(err) - } - config.Fragment.PacketsFrom = uint64(from) - config.Fragment.PacketsTo = uint64(to) - if config.Fragment.PacketsFrom == 0 { - return nil, errors.New("PacketsFrom can't be 0") - } - } - - { - if c.Fragment.Length == nil { - return nil, errors.New("Length can't be empty") - } - config.Fragment.LengthMin = uint64(c.Fragment.Length.From) - config.Fragment.LengthMax = uint64(c.Fragment.Length.To) - if config.Fragment.LengthMin == 0 { - return nil, errors.New("LengthMin can't be 0") - } - } - - { - if c.Fragment.Interval == nil { - return nil, errors.New("Interval can't be empty") - } - config.Fragment.IntervalMin = uint64(c.Fragment.Interval.From) - config.Fragment.IntervalMax = uint64(c.Fragment.Interval.To) - } - - { - if c.Fragment.MaxSplit != nil { - config.Fragment.MaxSplitMin = uint64(c.Fragment.MaxSplit.From) - config.Fragment.MaxSplitMax = uint64(c.Fragment.MaxSplit.To) - } - } + return nil, errors.PrintRemovedFeatureError("fragment", "finalmask/tcp fragment") } if c.Noise != nil { @@ -128,13 +65,6 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } if c.Noises != nil { - // for _, n := range c.Noises { - // NConfig, err := ParseNoise(n) - // if err != nil { - // return nil, err - // } - // config.Noises = append(config.Noises, NConfig) - // } return nil, errors.PrintRemovedFeatureError("noise", "finalmask/udp noise") } @@ -163,59 +93,3 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } return config, nil } - -// func ParseNoise(noise *Noise) (*freedom.Noise, error) { -// var err error -// NConfig := new(freedom.Noise) -// noise.Packet = strings.TrimSpace(noise.Packet) - -// switch noise.Type { -// case "rand": -// min, max, err := ParseRangeString(noise.Packet) -// if err != nil { -// return nil, errors.New("invalid value for rand Length").Base(err) -// } -// NConfig.LengthMin = uint64(min) -// NConfig.LengthMax = uint64(max) -// if NConfig.LengthMin == 0 { -// return nil, errors.New("rand lengthMin or lengthMax cannot be 0") -// } - -// case "str": -// // user input string -// NConfig.Packet = []byte(noise.Packet) - -// case "hex": -// // user input hex -// NConfig.Packet, err = hex.DecodeString(noise.Packet) -// if err != nil { -// return nil, errors.New("Invalid hex string").Base(err) -// } - -// case "base64": -// // user input base64 -// NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet)) -// if err != nil { -// return nil, errors.New("Invalid base64 string").Base(err) -// } - -// default: -// return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported") -// } - -// if noise.Delay != nil { -// NConfig.DelayMin = uint64(noise.Delay.From) -// NConfig.DelayMax = uint64(noise.Delay.To) -// } -// switch strings.ToLower(noise.ApplyTo) { -// case "", "ip", "all": -// NConfig.ApplyTo = "ip" -// case "ipv4": -// NConfig.ApplyTo = "ipv4" -// case "ipv6": -// NConfig.ApplyTo = "ipv6" -// default: -// return nil, errors.New("Invalid applyTo, only ip/ipv4/ipv6 are supported") -// } -// return NConfig, nil -// } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 7945d0bfe293..d1e0bb26162c 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -18,6 +18,7 @@ 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/fragment" "github.com/xtls/xray-core/transport/internet/finalmask/header/custom" "github.com/xtls/xray-core/transport/internet/finalmask/header/dns" "github.com/xtls/xray-core/transport/internet/finalmask/header/dtls" @@ -1361,6 +1362,50 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { }, nil } +type Fragment struct { + Packets string `json:"packets"` + Length Int32Range `json:"length"` + Delay Int32Range `json:"delay"` + MaxSplit Int32Range `json:"maxSplit"` +} + +func (c *Fragment) Build() (proto.Message, error) { + config := &fragment.Config{} + + switch strings.ToLower(c.Packets) { + case "tlshello": + config.PacketsFrom = 0 + config.PacketsTo = 1 + case "": + config.PacketsFrom = 0 + config.PacketsTo = 0 + default: + from, to, err := ParseRangeString(c.Packets) + if err != nil { + return nil, errors.New("Invalid PacketsFrom").Base(err) + } + config.PacketsFrom = int64(from) + config.PacketsTo = int64(to) + if config.PacketsFrom == 0 { + return nil, errors.New("PacketsFrom can't be 0") + } + } + + config.LengthMin = int64(c.Length.From) + config.LengthMax = int64(c.Length.To) + if config.LengthMin == 0 { + return nil, errors.New("LengthMin can't be 0") + } + + config.DelayMin = int64(c.Delay.From) + config.DelayMax = int64(c.Delay.To) + + config.MaxSplitMin = int64(c.MaxSplit.From) + config.MaxSplitMax = int64(c.MaxSplit.To) + + return config, nil +} + type NoiseItem struct { Rand Int32Range `json:"rand"` Packet []byte `json:"packet"` diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index d107e357157b..f0247aa39d84 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -2,14 +2,10 @@ package freedom import ( "context" - "crypto/rand" - "io" - "time" "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" @@ -178,28 +174,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var writer buf.Writer if destination.Network == net.Network_TCP { - if h.config.Fragment != nil { - errors.LogDebug(ctx, "FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax, - h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax, h.config.Fragment.MaxSplitMin, h.config.Fragment.MaxSplitMax) - writer = buf.NewWriter(&FragmentWriter{ - fragment: h.config.Fragment, - writer: conn, - }) - } else { - writer = buf.NewWriter(conn) - } + writer = buf.NewWriter(conn) } else { writer = NewPacketWriter(conn, h, UDPOverride, destination) - if h.config.Noises != nil { - errors.LogDebug(ctx, "NOISE", h.config.Noises) - writer = &NoisePacketWriter{ - Writer: writer, - noises: h.config.Noises, - firstWrite: true, - UDPOverride: UDPOverride, - remoteAddr: net.DestinationFromAddr(conn.RemoteAddr()).Address, - } - } } if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { @@ -419,161 +396,3 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } return nil } - -type NoisePacketWriter struct { - buf.Writer - noises []*Noise - firstWrite bool - UDPOverride net.Destination - remoteAddr net.Address -} - -// MultiBuffer writer with Noise before first packet -func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { - if w.firstWrite { - w.firstWrite = false - //Do not send Noise for dns requests(just to be safe) - if w.UDPOverride.Port == 53 { - return w.Writer.WriteMultiBuffer(mb) - } - var noise []byte - var err error - if w.remoteAddr.Family().IsDomain() { - panic("impossible, remoteAddr is always IP") - } - for _, n := range w.noises { - switch n.ApplyTo { - case "ipv4": - if w.remoteAddr.Family().IsIPv6() { - continue - } - case "ipv6": - if w.remoteAddr.Family().IsIPv4() { - continue - } - case "ip": - default: - panic("unreachable, applyTo is ip/ipv4/ipv6") - } - //User input string or base64 encoded string or hex string - if n.Packet != nil { - noise = n.Packet - } else { - //Random noise - noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin), - int64(n.LengthMax))) - } - if err != nil { - return err - } - err = w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) - if err != nil { - return err - } - - if n.DelayMin != 0 || n.DelayMax != 0 { - time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond) - } - } - - } - return w.Writer.WriteMultiBuffer(mb) -} - -type FragmentWriter struct { - fragment *Fragment - writer io.Writer - count uint64 -} - -func (f *FragmentWriter) Write(b []byte) (int, error) { - f.count++ - - if f.fragment.PacketsFrom == 0 && f.fragment.PacketsTo == 1 { - if f.count != 1 || len(b) <= 5 || b[0] != 22 { - return f.writer.Write(b) - } - recordLen := 5 + ((int(b[3]) << 8) | int(b[4])) - if len(b) < recordLen { // maybe already fragmented somehow - return f.writer.Write(b) - } - data := b[5:recordLen] - buff := make([]byte, 2048) - var hello []byte - maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax)) - var splitNum int64 - for from := 0; ; { - to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) - splitNum++ - if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) { - to = len(data) - } - l := to - from - if 5+l > len(buff) { - buff = make([]byte, 5+l) - } - copy(buff[:3], b) - copy(buff[5:], data[from:to]) - from = to - buff[3] = byte(l >> 8) - buff[4] = byte(l) - if f.fragment.IntervalMax == 0 { // combine fragmented tlshello if interval is 0 - hello = append(hello, buff[:5+l]...) - } else { - _, err := f.writer.Write(buff[:5+l]) - time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) - if err != nil { - return 0, err - } - } - if from == len(data) { - if len(hello) > 0 { - _, err := f.writer.Write(hello) - if err != nil { - return 0, err - } - } - if len(b) > recordLen { - n, err := f.writer.Write(b[recordLen:]) - if err != nil { - return recordLen + n, err - } - } - return len(b), nil - } - } - } - - if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) { - return f.writer.Write(b) - } - maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax)) - var splitNum int64 - for from := 0; ; { - to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) - splitNum++ - if to > len(b) || (maxSplit > 0 && splitNum >= maxSplit) { - to = len(b) - } - n, err := f.writer.Write(b[from:to]) - from += n - if err != nil { - return from, err - } - time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) - if from >= len(b) { - return from, nil - } - } -} - -func GenerateRandomBytes(n int64) ([]byte, error) { - b := make([]byte, n) - _, err := rand.Read(b) - // Note that err == nil only if we read len(b) bytes. - if err != nil { - return nil, err - } - - return b, nil -} From 7b4f819ba8af9305450fbdfce5eee5d82f6e0a48 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 16:49:19 +0800 Subject: [PATCH 19/75] refactor tcp custom header --- .../internet/finalmask/header/custom/tcp.go | 133 +++++++++++++----- transport/internet/finalmask/tcp_test.go | 8 +- transport/internet/finalmask/xdns/server.go | 6 +- 3 files changed, 105 insertions(+), 42 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index ebff7ce17b69..713c3844d6cb 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -16,6 +16,7 @@ import ( type tcpCustomClient struct { clients []*TCPSequence servers []*TCPSequence + merged [][]byte } type tcpCustomClientConn struct { @@ -36,18 +37,13 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for _, client := range conn.header.clients { + conn.header.merged = make([][]byte, len(conn.header.clients)) + for index, client := range conn.header.clients { for _, item := range client.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, item.Rand) - } - } - } - - for _, server := range conn.header.servers { - for _, item := range server.Sequence { - if item.Rand > 0 { - item.Packet = make([]byte, item.Rand) + conn.header.merged[index] = append(conn.header.merged[index], make([]byte, item.Rand)...) + } else { + conn.header.merged[index] = append(conn.header.merged[index], item.Packet...) } } } @@ -82,26 +78,48 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { i := 0 j := 0 for i = range c.header.clients { - for _, item := range c.header.clients[i].Sequence { - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + index := 0 + from := 0 + to := 0 + for to < len(c.header.merged[i]) { + item := c.header.clients[i].Sequence[index] + if item.DelayMax > 0 { + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[i][from:to]) + if err != nil { + c.wg.Done() + return + } + from = to + } + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + length := max(int(item.Rand), len(item.Packet)) if item.Rand > 0 { - common.Must2(rand.Read(item.Packet)) + common.Must2(rand.Read(c.header.merged[i][to : to+length])) } - n, err = c.Conn.Write(item.Packet) + to += length + index++ + } + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[i][from:to]) if err != nil { c.wg.Done() return } + from = to } + if j < len(c.header.servers) { for _, item := range c.header.servers[j].Sequence { - n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) + length := max(int(item.Rand), len(item.Packet)) + n, err := io.ReadFull(c.Conn, buf[:length]) if err != nil { c.wg.Done() return } if item.Rand > 0 { - if n != len(item.Packet) { + if n != length { c.wg.Done() return } @@ -116,13 +134,14 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { for j < len(c.header.servers) { for _, item := range c.header.servers[j].Sequence { - n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) + length := max(int(item.Rand), len(item.Packet)) + n, err := io.ReadFull(c.Conn, buf[:length]) if err != nil { c.wg.Done() return } if item.Rand > 0 { - if n != len(item.Packet) { + if n != length { c.wg.Done() return } @@ -151,6 +170,7 @@ type tcpCustomServer struct { clients []*TCPSequence servers []*TCPSequence onError []byte + merged [][]byte } type tcpCustomServerConn struct { @@ -172,18 +192,13 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - for _, client := range conn.header.clients { + conn.header.merged = make([][]byte, len(conn.header.servers)) + for index, client := range conn.header.servers { for _, item := range client.Sequence { if item.Rand > 0 { - item.Packet = make([]byte, item.Rand) - } - } - } - - for _, server := range conn.header.servers { - for _, item := range server.Sequence { - if item.Rand > 0 { - item.Packet = make([]byte, item.Rand) + conn.header.merged[index] = append(conn.header.merged[index], make([]byte, item.Rand)...) + } else { + conn.header.merged[index] = append(conn.header.merged[index], item.Packet...) } } } @@ -209,13 +224,14 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { j := 0 for i = range c.header.clients { for _, item := range c.header.clients[i].Sequence { - n, err := io.ReadFull(c.Conn, buf[:len(item.Packet)]) + length := max(int(item.Rand), len(item.Packet)) + n, err := io.ReadFull(c.Conn, buf[:length]) if err != nil { c.wg.Done() return } if item.Rand > 0 { - if n != len(item.Packet) { + if n != length { if len(c.header.onError) > 0 { c.Conn.Write(c.header.onError) } @@ -230,33 +246,74 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { return } } + if j < len(c.header.servers) { - for _, item := range c.header.servers[i].Sequence { - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + index := 0 + from := 0 + to := 0 + for to < len(c.header.merged[j]) { + item := c.header.servers[j].Sequence[index] + if item.DelayMax > 0 { + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[j][from:to]) + if err != nil { + c.wg.Done() + return + } + from = to + } + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + length := max(int(item.Rand), len(item.Packet)) if item.Rand > 0 { - common.Must2(rand.Read(item.Packet)) + common.Must2(rand.Read(c.header.merged[j][to : to+length])) } - n, err = c.Conn.Write(item.Packet) + to += length + index++ + } + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() return } + from = to } j++ } } for j < len(c.header.servers) { - for _, item := range c.header.servers[i].Sequence { - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + index := 0 + from := 0 + to := 0 + for to < len(c.header.merged[j]) { + item := c.header.servers[j].Sequence[index] + if item.DelayMax > 0 { + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[j][from:to]) + if err != nil { + c.wg.Done() + return + } + from = to + } + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + length := max(int(item.Rand), len(item.Packet)) if item.Rand > 0 { - common.Must2(rand.Read(item.Packet)) + common.Must2(rand.Read(c.header.merged[j][to : to+length])) } - n, err = c.Conn.Write(item.Packet) + to += length + index++ + } + if to-from > 0 { + _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() return } + from = to } j++ } diff --git a/transport/internet/finalmask/tcp_test.go b/transport/internet/finalmask/tcp_test.go index c144c1c71387..7febb185695d 100644 --- a/transport/internet/finalmask/tcp_test.go +++ b/transport/internet/finalmask/tcp_test.go @@ -37,7 +37,7 @@ func mustSendRecvTcp( } if !bytes.Equal(buf[:n], msg) { - t.Fatalf("unexpected data") + t.Fatalf("unexpected data %q", buf[:n]) } } @@ -57,6 +57,9 @@ func TestConnReadWrite(t *testing.T) { { Packet: []byte{1}, }, + { + Rand: 1, + }, }, }, }, @@ -66,6 +69,9 @@ func TestConnReadWrite(t *testing.T) { { Packet: []byte{2}, }, + { + Rand: 1, + }, }, }, }, diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 2a5ec6cb8fe3..5102eff69a9e 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -42,7 +42,7 @@ type record struct { } type queue struct { - lash time.Time + last time.Time queue chan []byte stash chan []byte } @@ -99,7 +99,7 @@ func (c *xdnsConnServer) clean() { now := time.Now() for key, q := range c.writeQueueMap { - if now.Sub(q.lash) >= idleTimeout { + if now.Sub(q.last) >= idleTimeout { close(q.queue) close(q.stash) delete(c.writeQueueMap, key) @@ -133,7 +133,7 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { } c.writeQueueMap[addr.String()] = q } - q.lash = time.Now() + q.last = time.Now() return q } From 5c3afcdb4fe705aa23db5c443b78d652e60a882f Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 16:54:45 +0800 Subject: [PATCH 20/75] 1 --- transport/internet/finalmask/header/custom/tcp.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 713c3844d6cb..6a6f4ad705be 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -84,7 +84,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { for to < len(c.header.merged[i]) { item := c.header.clients[i].Sequence[index] if item.DelayMax > 0 { - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[i][from:to]) if err != nil { c.wg.Done() @@ -101,7 +101,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { to += length index++ } - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[i][from:to]) if err != nil { c.wg.Done() @@ -254,7 +254,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { for to < len(c.header.merged[j]) { item := c.header.servers[j].Sequence[index] if item.DelayMax > 0 { - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() @@ -271,7 +271,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { to += length index++ } - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() @@ -290,7 +290,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { for to < len(c.header.merged[j]) { item := c.header.servers[j].Sequence[index] if item.DelayMax > 0 { - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() @@ -307,7 +307,7 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { to += length index++ } - if to-from > 0 { + if to > from { _, err := c.Conn.Write(c.header.merged[j][from:to]) if err != nil { c.wg.Done() From 72abbd02d89623507325e376771875fca0448d94 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 19:55:30 +0800 Subject: [PATCH 21/75] refactor udpmask readfrom --- .../internet/finalmask/header/custom/udp.go | 131 ++++++++++++------ .../internet/finalmask/header/dns/conn.go | 46 +++--- .../internet/finalmask/header/dtls/conn.go | 46 +++--- .../internet/finalmask/header/srtp/conn.go | 46 +++--- .../internet/finalmask/header/utp/conn.go | 46 +++--- .../internet/finalmask/header/wechat/conn.go | 46 +++--- .../finalmask/header/wireguard/conn.go | 46 +++--- .../internet/finalmask/mkcp/aes128gcm/conn.go | 60 ++++---- .../internet/finalmask/mkcp/original/conn.go | 60 ++++---- .../internet/finalmask/salamander/conn.go | 44 +++--- 10 files changed, 350 insertions(+), 221 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index a735c372a38b..b11c78888b80 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -2,6 +2,7 @@ package custom import ( "bytes" + "context" "crypto/rand" "io" "net" @@ -33,6 +34,7 @@ func (h *udpCustomClient) Serialize(b []byte) { type udpCustomClientConn struct { first bool leaveSize int32 + closed bool net.PacketConn header *udpCustomClient @@ -82,35 +84,48 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro if c.first { c.readMutex.Lock() - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - c.readMutex.Unlock() - return n, addr, err - } - - index := 0 - for _, item := range c.header.server { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { + for { + if c.closed { c.readMutex.Unlock() - return 0, addr, errors.New("header mismatch") + return 0, nil, io.EOF } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } + + index := 0 + mismatch := false + for _, item := range c.header.server { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + mismatch = true + break + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + mismatch = true + break + } + index += length + } + + if mismatch { + errors.LogDebug(context.Background(), "mask read err header mismatch") + continue + } + + if len(p) < n-index { c.readMutex.Unlock() - return 0, addr, errors.New("header mismatch") + return 0, nil, io.ErrShortBuffer } - index += length - } - if len(p) < n-index { + copy(p, c.readBuf[index:n]) + c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return n - index, addr, nil } - - copy(p, c.readBuf[index:n]) - - c.readMutex.Unlock() - return n - index, addr, err } n, addr, err = c.PacketConn.ReadFrom(p) @@ -132,7 +147,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro copy(p, p[index:n]) - return n - index, addr, err + return n - index, addr, nil } func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -169,6 +184,11 @@ func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error return c.PacketConn.WriteTo(p, addr) } +func (c *udpCustomClientConn) Close() error { + c.closed = true + return c.PacketConn.Close() +} + type udpCustomServer struct { client []*UDPItem server []*UDPItem @@ -191,6 +211,7 @@ func (h *udpCustomServer) Serialize(b []byte) { type udpCustomServerConn struct { first bool leaveSize int32 + closed bool net.PacketConn header *udpCustomServer @@ -240,35 +261,48 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro if c.first { c.readMutex.Lock() - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - c.readMutex.Unlock() - return n, addr, err - } - - index := 0 - for _, item := range c.header.client { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { + for { + if c.closed { c.readMutex.Unlock() - return 0, addr, errors.New("header mismatch") + return 0, nil, io.EOF } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } + + index := 0 + mismatch := false + for _, item := range c.header.client { + length := max(int(item.Rand), len(item.Packet)) + if index+length > n { + mismatch = true + break + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { + mismatch = true + break + } + index += length + } + + if mismatch { + errors.LogDebug(context.Background(), "mask read err header mismatch") + continue + } + + if len(p) < n-index { c.readMutex.Unlock() - return 0, addr, errors.New("header mismatch") + return 0, nil, io.ErrShortBuffer } - index += length - } - if len(p) < n-index { + copy(p, c.readBuf[index:n]) + c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return n - index, addr, nil } - - copy(p, c.readBuf[index:n]) - - c.readMutex.Unlock() - return n - index, addr, err } n, addr, err = c.PacketConn.ReadFrom(p) @@ -290,7 +324,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro copy(p, p[index:n]) - return n - index, addr, err + return n - index, addr, nil } func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -326,3 +360,8 @@ func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error return c.PacketConn.WriteTo(p, addr) } + +func (c *udpCustomServerConn) Close() error { + c.closed = true + return c.PacketConn.Close() +} diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index 0baa8fec9972..f8b0aa23c0fa 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -1,6 +1,7 @@ package dns import ( + "context" "encoding/binary" "io" "net" @@ -93,6 +94,7 @@ func (h *dns) Serialize(b []byte) { type dnsConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *dns @@ -150,26 +152,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } - copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + copy(p, c.readBuf[c.Size():n]) + + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -178,12 +187,12 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -221,6 +230,7 @@ func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *dnsConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 8b12f83e3ea8..cae03d845b2c 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -1,6 +1,7 @@ package dtls import ( + "context" "io" "net" sync "sync" @@ -44,6 +45,7 @@ func (h *dtls) Serialize(b []byte) { type dtlsConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *dtls @@ -87,26 +89,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - copy(p, c.readBuf[c.Size():n]) + copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -115,12 +124,12 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -158,6 +167,7 @@ func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *dtlsConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index bb03bce70953..61343c501ac6 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -1,6 +1,7 @@ package srtp import ( + "context" "encoding/binary" "io" "net" @@ -29,6 +30,7 @@ func (h *srtp) Serialize(b []byte) { type srtpConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *srtp @@ -71,26 +73,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - copy(p, c.readBuf[c.Size():n]) + copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -99,12 +108,12 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -142,6 +151,7 @@ func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *srtpConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 5d48193ca74e..b887b5208755 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -1,6 +1,7 @@ package utp import ( + "context" "encoding/binary" "io" "net" @@ -30,6 +31,7 @@ func (h *utp) Serialize(b []byte) { type utpConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *utp @@ -73,26 +75,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - copy(p, c.readBuf[c.Size():n]) + copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -101,12 +110,12 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -144,6 +153,7 @@ func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *utpConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index f1f56c49d72d..9b077fbbda71 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -1,6 +1,7 @@ package wechat import ( + "context" "encoding/binary" "io" "net" @@ -36,6 +37,7 @@ func (h *wechat) Serialize(b []byte) { type wechatConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *wechat @@ -77,26 +79,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - copy(p, c.readBuf[c.Size():n]) + copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -105,12 +114,12 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -148,6 +157,7 @@ func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *wechatConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index f4bf17748242..98dadbb51125 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -1,6 +1,7 @@ package wireguard import ( + "context" "io" "net" sync "sync" @@ -25,6 +26,7 @@ func (h *wireguare) Serialize(b []byte) { type wireguareConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn header *wireguare @@ -64,26 +66,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - copy(p, c.readBuf[c.Size():n]) + copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -92,12 +101,12 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("header").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } copy(p, p[c.Size():n]) - return n - int(c.Size()), addr, err + return n - int(c.Size()), addr, nil } func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -135,6 +144,7 @@ func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *wireguareConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 9f36fc2adeaf..89b7e9bcdf3e 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -1,6 +1,7 @@ package aes128gcm import ( + "context" "crypto/cipher" "crypto/rand" "crypto/sha256" @@ -17,6 +18,7 @@ import ( type aes128gcmConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn aead cipher.AEAD @@ -58,33 +60,40 @@ 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) - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } + + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, 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 { + errors.LogDebug(context.Background(), "mask read err aead open ", err) + continue + } - if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + return n - int(c.Size()), addr, nil } - - 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) @@ -93,7 +102,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } nonceSize := c.aead.NonceSize() @@ -154,6 +163,7 @@ func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *aes128gcmConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index c97c8d7c8712..ac2d5116a6f7 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -1,6 +1,7 @@ package original import ( + "context" "crypto/cipher" "encoding/binary" "hash/fnv" @@ -77,6 +78,7 @@ func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) { type simpleConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn aead cipher.AEAD @@ -116,33 +118,40 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } + + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } + + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } + + ciphertext := c.readBuf[:n] + opened, err := c.aead.Open(nil, nil, ciphertext, nil) + if err != nil { + errors.LogDebug(context.Background(), "mask read err aead open ", err) + continue + } + + copy(p, opened) - if n < int(c.Size()) { c.readMutex.Unlock() - return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + return n - int(c.Size()), addr, nil } - - 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) @@ -151,7 +160,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("aead").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } ciphertext := p[:n] @@ -205,6 +214,7 @@ func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *simpleConn) Close() error { + c.closed = true return c.conn.Close() } diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 154a6aa33312..076e88e99eb0 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -1,6 +1,7 @@ package salamander import ( + "context" "io" "net" "sync" @@ -12,6 +13,7 @@ import ( type obfsPacketConn struct { first bool leaveSize int32 + closed bool conn net.PacketConn obfs *SalamanderObfuscator @@ -56,26 +58,33 @@ 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 - } + for { + if c.closed { + c.readMutex.Unlock() + return 0, nil, io.EOF + } - if n < int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) - } + n, addr, err = c.conn.ReadFrom(c.readBuf) + if err != nil { + errors.LogDebug(context.Background(), "mask read err ", err) + continue + } - if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) - } + if n < int(c.Size()) { + errors.LogDebug(context.Background(), "mask read err short lenth") + continue + } + + if len(p) < n-int(c.Size()) { + c.readMutex.Unlock() + return 0, nil, io.ErrShortBuffer + } - c.obfs.Deobfuscate(c.readBuf[:n], p) + c.obfs.Deobfuscate(c.readBuf[:n], p) - c.readMutex.Unlock() - return n - int(c.Size()), addr, err + c.readMutex.Unlock() + return n - int(c.Size()), addr, nil + } } n, addr, err = c.conn.ReadFrom(p) @@ -84,7 +93,7 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if n < int(c.Size()) { - return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer) + return 0, addr, errors.New("short lenth") } c.obfs.Deobfuscate(p[:n], p) @@ -127,6 +136,7 @@ func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } func (c *obfsPacketConn) Close() error { + c.closed = true return c.conn.Close() } From ecdcb6193216371d58cc84e8366cf131542b592e Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 20:05:10 +0800 Subject: [PATCH 22/75] addr log --- transport/internet/finalmask/header/custom/udp.go | 8 ++++---- transport/internet/finalmask/header/dns/conn.go | 4 ++-- transport/internet/finalmask/header/dtls/conn.go | 4 ++-- transport/internet/finalmask/header/srtp/conn.go | 4 ++-- transport/internet/finalmask/header/utp/conn.go | 4 ++-- transport/internet/finalmask/header/wechat/conn.go | 4 ++-- transport/internet/finalmask/header/wireguard/conn.go | 4 ++-- transport/internet/finalmask/mkcp/aes128gcm/conn.go | 6 +++--- transport/internet/finalmask/mkcp/original/conn.go | 6 +++--- transport/internet/finalmask/salamander/conn.go | 4 ++-- transport/internet/finalmask/xdns/client.go | 2 +- transport/internet/finalmask/xdns/server.go | 2 +- transport/internet/finalmask/xicmp/client.go | 2 +- transport/internet/finalmask/xicmp/server.go | 2 +- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index b11c78888b80..6704626e58ef 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -92,7 +92,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -112,7 +112,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro } if mismatch { - errors.LogDebug(context.Background(), "mask read err header mismatch") + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") continue } @@ -269,7 +269,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -289,7 +289,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro } if mismatch { - errors.LogDebug(context.Background(), "mask read err header mismatch") + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") continue } diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index f8b0aa23c0fa..b26a42be8b9b 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -160,12 +160,12 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index cae03d845b2c..468f0805cf3a 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -97,12 +97,12 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 61343c501ac6..fae99da8e2d6 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -81,12 +81,12 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index b887b5208755..8c0ced3fe031 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -83,12 +83,12 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index 9b077fbbda71..fc42f034f4fc 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -87,12 +87,12 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 98dadbb51125..4ff0940a76b0 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -74,12 +74,12 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 89b7e9bcdf3e..16517b2ee20c 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -68,12 +68,12 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } @@ -87,7 +87,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { ciphertext := c.readBuf[nonceSize:n] _, err = c.aead.Open(p[:0], nonce, ciphertext, nil) if err != nil { - errors.LogDebug(context.Background(), "mask read err aead open ", err) + errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) continue } diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index ac2d5116a6f7..c7841a1065d8 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -126,12 +126,12 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } @@ -143,7 +143,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { ciphertext := c.readBuf[:n] opened, err := c.aead.Open(nil, nil, ciphertext, nil) if err != nil { - errors.LogDebug(context.Background(), "mask read err aead open ", err) + errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) continue } diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 076e88e99eb0..a1e7ece5fee8 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -66,12 +66,12 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.conn.ReadFrom(c.readBuf) if err != nil { - errors.LogDebug(context.Background(), "mask read err ", err) + errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } if n < int(c.Size()) { - errors.LogDebug(context.Background(), "mask read err short lenth") + errors.LogDebug(context.Background(), addr, " mask read err short lenth") continue } diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 9d80bc225762..70e375768f3b 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -194,7 +194,7 @@ func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { - return 0, nil, io.ErrShortBuffer + return 0, addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 5102eff69a9e..817b709471f9 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -319,7 +319,7 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { - return 0, nil, io.ErrShortBuffer + return 0, addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 9f02fca34f0f..780d04e9ba14 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -290,7 +290,7 @@ func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { - return 0, nil, io.ErrShortBuffer + return 0, addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 8d2ee256ef74..4ab5ebad142b 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -313,7 +313,7 @@ func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { - return 0, nil, io.ErrShortBuffer + return 0, addr, io.ErrShortBuffer } return n, packet.addr, nil } From e79839230cbc38330ff895bb9d950f0eb8856989 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 20:16:28 +0800 Subject: [PATCH 23/75] noise applyto --- .../internet/finalmask/noise/config.pb.go | 17 ++++++++++--- .../internet/finalmask/noise/config.proto | 3 ++- transport/internet/finalmask/noise/conn.go | 25 +++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/transport/internet/finalmask/noise/config.pb.go b/transport/internet/finalmask/noise/config.pb.go index 16e1b25328fd..0dd833c044b8 100644 --- a/transport/internet/finalmask/noise/config.pb.go +++ b/transport/internet/finalmask/noise/config.pb.go @@ -101,7 +101,8 @@ type Config struct { state protoimpl.MessageState `protogen:"open.v1"` ResetMin int64 `protobuf:"varint,1,opt,name=reset_min,json=resetMin,proto3" json:"reset_min,omitempty"` ResetMax int64 `protobuf:"varint,2,opt,name=reset_max,json=resetMax,proto3" json:"reset_max,omitempty"` - Items []*Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + ApplyTo string `protobuf:"bytes,3,opt,name=apply_to,json=applyTo,proto3" json:"apply_to,omitempty"` + Items []*Item `protobuf:"bytes,4,rep,name=items,proto3" json:"items,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -150,6 +151,13 @@ func (x *Config) GetResetMax() int64 { return 0 } +func (x *Config) GetApplyTo() string { + if x != nil { + return x.ApplyTo + } + return "" +} + func (x *Config) GetItems() []*Item { if x != nil { return x.Items @@ -167,11 +175,12 @@ const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" + "\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" + "\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" + "\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" + - "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" + + "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\xa2\x01\n" + "\x06Config\x12\x1b\n" + "\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" + - "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" + - "\x05items\x18\x03 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" + + "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12\x19\n" + + "\bapply_to\x18\x03 \x01(\tR\aapplyTo\x12C\n" + + "\x05items\x18\x04 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" + "+com.xray.transport.internet.finalmask.noiseP\x01Z Date: Fri, 13 Feb 2026 20:22:36 +0800 Subject: [PATCH 24/75] ApplyTo --- transport/internet/finalmask/noise/conn.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go index 1b4bfb250561..a15078254a07 100644 --- a/transport/internet/finalmask/noise/conn.go +++ b/transport/internet/finalmask/noise/conn.go @@ -69,14 +69,20 @@ func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.mutex.RUnlock() switch c.config.ApplyTo { - case "ipv4": - if v6udp(addr) { + case "", "udp": + if !udp(addr) { ready = true } - case "ipv6": - if v4udp(addr) { + case "v4udp": + if !v4udp(addr) { ready = true } + case "v6udp": + if !v6udp(addr) { + ready = true + } + default: + ready = true } if !ready { @@ -106,6 +112,11 @@ func (c *noiseConn) Close() error { return c.PacketConn.Close() } +func udp(addr net.Addr) bool { + _, ok := addr.(*net.UDPAddr) + return ok +} + func v4udp(addr net.Addr) bool { if v, ok := addr.(*net.UDPAddr); ok { return v.IP.To4() != nil From 9ee4d93e5c0e96d6369e6733f50800c33fa8b762 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 21:04:55 +0800 Subject: [PATCH 25/75] noise reset logic --- transport/internet/finalmask/noise/conn.go | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go index a15078254a07..d606ac49331e 100644 --- a/transport/internet/finalmask/noise/conn.go +++ b/transport/internet/finalmask/noise/conn.go @@ -14,7 +14,7 @@ import ( type noiseConn struct { net.PacketConn config *Config - m map[string]struct{} + m map[string]time.Time stop chan struct{} once sync.Once mutex sync.RWMutex @@ -28,7 +28,7 @@ func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err conn := &noiseConn{ PacketConn: raw, config: c, - m: make(map[string]struct{}), + m: make(map[string]time.Time), stop: make(chan struct{}), } @@ -44,15 +44,26 @@ func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err } func (c *noiseConn) reset() { - ticker := time.NewTicker(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second) + ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: - c.mutex.Lock() - c.m = make(map[string]struct{}) - c.mutex.Unlock() - ticker.Reset(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second) + c.mutex.RLock() + now := time.Now() + timeOut := make([]string, 0, len(c.m)) + for key, last := range c.m { + if now.After(last) { + timeOut = append(timeOut, key) + } + } + c.mutex.RUnlock() + + for _, key := range timeOut { + c.mutex.Lock() + delete(c.m, key) + c.mutex.Unlock() + } case <-c.stop: return } @@ -97,7 +108,7 @@ func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.PacketConn.WriteTo(item.Packet, addr) time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) } - c.m[addr.String()] = struct{}{} + c.m[addr.String()] = time.Now().Add(time.Duration(crypto.RandBetween(c.config.ResetMin, c.config.ResetMax)) * time.Second) } c.mutex.Unlock() } From 59bdbcc740ea683b401f63fe3bea6d34a13e104a Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 21:26:16 +0800 Subject: [PATCH 26/75] v4udp --- transport/internet/finalmask/finalmask.go | 12 ++++++++++ transport/internet/finalmask/noise/conn.go | 26 ++++------------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index cc95cf03907c..5a026d6666ba 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -104,3 +104,15 @@ func UnwrapTcpMask(conn net.Conn) net.Conn { } } } + +func Udp(addr net.Addr) bool { + _, ok := addr.(*net.UDPAddr) + return ok +} + +func V6udp(addr net.Addr) bool { + if v, ok := addr.(*net.UDPAddr); ok { + return v.IP.To16() != nil && len(v.IP.To4()) == 0 + } + return false +} diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go index d606ac49331e..74fe25525c9b 100644 --- a/transport/internet/finalmask/noise/conn.go +++ b/transport/internet/finalmask/noise/conn.go @@ -9,6 +9,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type noiseConn struct { @@ -81,15 +82,15 @@ func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { switch c.config.ApplyTo { case "", "udp": - if !udp(addr) { + if !finalmask.Udp(addr) { ready = true } case "v4udp": - if !v4udp(addr) { + if !finalmask.Udp(addr) || finalmask.Udp(addr) && finalmask.V6udp(addr) { ready = true } case "v6udp": - if !v6udp(addr) { + if !finalmask.V6udp(addr) { ready = true } default: @@ -122,22 +123,3 @@ func (c *noiseConn) Close() error { }) return c.PacketConn.Close() } - -func udp(addr net.Addr) bool { - _, ok := addr.(*net.UDPAddr) - return ok -} - -func v4udp(addr net.Addr) bool { - if v, ok := addr.(*net.UDPAddr); ok { - return v.IP.To4() != nil - } - return false -} - -func v6udp(addr net.Addr) bool { - if v, ok := addr.(*net.UDPAddr); ok { - return v.IP.To16() != nil && v.IP.To4() == nil - } - return false -} From 15c2e70c759970eac0673ce6b24edb90975acbe8 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 21:33:22 +0800 Subject: [PATCH 27/75] xhttp3 mask --- transport/internet/splithttp/dialer.go | 9 +++++++++ transport/internet/splithttp/hub.go | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 2c92dbd5897c..ffb501497a17 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -195,6 +195,15 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea } } + if streamSettings.UdpmaskManager != nil { + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(udpConn) + if err != nil { + udpConn.Close() + return nil, errors.New("mask err").Base(err) + } + udpConn = pktConn + } + return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg) }, } diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index 7e15724646c5..fe2a1822a692 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -472,6 +472,16 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet if err != nil { return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err) } + + if streamSettings.UdpmaskManager != nil { + pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnServer(Conn) + if err != nil { + Conn.Close() + return nil, errors.New("mask err").Base(err) + } + Conn = pktConn + } + l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, nil) if err != nil { return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err) From b74c1ff71d36c8d41f1029c2723491f330a72f21 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 22:08:03 +0800 Subject: [PATCH 28/75] noise infra --- infra/conf/transport_internet.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index d1e0bb26162c..96dc77d99125 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1413,8 +1413,9 @@ type NoiseItem struct { } type Noise struct { - Reset Int32Range `json:"reset"` - Noise []NoiseItem `json:"noise"` + Reset Int32Range `json:"reset"` + ApplyTo string `json:"applyTo"` + Noise []NoiseItem `json:"noise"` } func (c *Noise) Build() (proto.Message, error) { @@ -1432,6 +1433,7 @@ func (c *Noise) Build() (proto.Message, error) { return &noise.Config{ ResetMin: int64(c.Reset.From), ResetMax: int64(c.Reset.To), + ApplyTo: strings.ToLower(c.ApplyTo), Items: noiseSlice, }, nil } From 2a93a98b714da076d1a50986d900db1868acb2b3 Mon Sep 17 00:00:00 2001 From: null Date: Fri, 13 Feb 2026 22:49:52 +0800 Subject: [PATCH 29/75] fix infra --- infra/conf/transport_internet.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 96dc77d99125..5b2ca7431ec8 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1333,6 +1333,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { clients := make([]*custom.TCPSequence, len(c.Clients)) for i, value := range c.Clients { + clients[i] = &custom.TCPSequence{} for _, item := range value { clients[i].Sequence = append(clients[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), @@ -1345,6 +1346,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { servers := make([]*custom.TCPSequence, len(c.Servers)) for i, value := range c.Servers { + servers[i] = &custom.TCPSequence{} for _, item := range value { servers[i].Sequence = append(servers[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), From 9542327863e3cc45d9589272707b6d17bd4b779d Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 10:33:53 +0800 Subject: [PATCH 30/75] infra --- infra/conf/transport_internet.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 5b2ca7431ec8..935fda915c08 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1318,6 +1318,9 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { if len(item.Packet) > 8192 || item.Rand > 8192 { return nil, errors.New("len > 8192") } + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } } } for _, value := range c.Servers { @@ -1325,6 +1328,9 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { if len(item.Packet) > 8192 || item.Rand > 8192 { return nil, errors.New("len > 8192") } + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } } } if len(c.OnError) > 8192 { @@ -1421,6 +1427,12 @@ type Noise struct { } func (c *Noise) Build() (proto.Message, error) { + for _, item := range c.Noise { + if len(item.Packet) > 0 && item.Rand.To > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand.To > 0") + } + } + noiseSlice := make([]*noise.Item, 0, len(c.Noise)) for _, item := range c.Noise { noiseSlice = append(noiseSlice, &noise.Item{ @@ -1451,6 +1463,17 @@ type HeaderCustomUDP struct { } func (c *HeaderCustomUDP) Build() (proto.Message, error) { + for _, item := range c.Client { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + for _, item := range c.Server { + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } + client := make([]*custom.UDPItem, 0, len(c.Client)) for _, item := range c.Client { client = append(client, &custom.UDPItem{ From 0fd74b418b996cf4504491db7852b86654977c67 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 10:36:16 +0800 Subject: [PATCH 31/75] remove applyto --- infra/conf/transport_internet.go | 6 ++---- transport/internet/finalmask/finalmask.go | 12 ------------ .../internet/finalmask/noise/config.pb.go | 17 ++++------------- .../internet/finalmask/noise/config.proto | 3 +-- transport/internet/finalmask/noise/conn.go | 18 ------------------ 5 files changed, 7 insertions(+), 49 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 935fda915c08..ce7160ad6bcf 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1421,9 +1421,8 @@ type NoiseItem struct { } type Noise struct { - Reset Int32Range `json:"reset"` - ApplyTo string `json:"applyTo"` - Noise []NoiseItem `json:"noise"` + Reset Int32Range `json:"reset"` + Noise []NoiseItem `json:"noise"` } func (c *Noise) Build() (proto.Message, error) { @@ -1447,7 +1446,6 @@ func (c *Noise) Build() (proto.Message, error) { return &noise.Config{ ResetMin: int64(c.Reset.From), ResetMax: int64(c.Reset.To), - ApplyTo: strings.ToLower(c.ApplyTo), Items: noiseSlice, }, nil } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 5a026d6666ba..cc95cf03907c 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -104,15 +104,3 @@ func UnwrapTcpMask(conn net.Conn) net.Conn { } } } - -func Udp(addr net.Addr) bool { - _, ok := addr.(*net.UDPAddr) - return ok -} - -func V6udp(addr net.Addr) bool { - if v, ok := addr.(*net.UDPAddr); ok { - return v.IP.To16() != nil && len(v.IP.To4()) == 0 - } - return false -} diff --git a/transport/internet/finalmask/noise/config.pb.go b/transport/internet/finalmask/noise/config.pb.go index 0dd833c044b8..16e1b25328fd 100644 --- a/transport/internet/finalmask/noise/config.pb.go +++ b/transport/internet/finalmask/noise/config.pb.go @@ -101,8 +101,7 @@ type Config struct { state protoimpl.MessageState `protogen:"open.v1"` ResetMin int64 `protobuf:"varint,1,opt,name=reset_min,json=resetMin,proto3" json:"reset_min,omitempty"` ResetMax int64 `protobuf:"varint,2,opt,name=reset_max,json=resetMax,proto3" json:"reset_max,omitempty"` - ApplyTo string `protobuf:"bytes,3,opt,name=apply_to,json=applyTo,proto3" json:"apply_to,omitempty"` - Items []*Item `protobuf:"bytes,4,rep,name=items,proto3" json:"items,omitempty"` + Items []*Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -151,13 +150,6 @@ func (x *Config) GetResetMax() int64 { return 0 } -func (x *Config) GetApplyTo() string { - if x != nil { - return x.ApplyTo - } - return "" -} - func (x *Config) GetItems() []*Item { if x != nil { return x.Items @@ -175,12 +167,11 @@ const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" + "\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" + "\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" + "\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" + - "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\xa2\x01\n" + + "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" + "\x06Config\x12\x1b\n" + "\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" + - "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12\x19\n" + - "\bapply_to\x18\x03 \x01(\tR\aapplyTo\x12C\n" + - "\x05items\x18\x04 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" + + "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" + + "\x05items\x18\x03 \x03(\v2-.xray.transport.internet.finalmask.noise.ItemR\x05itemsB\x97\x01\n" + "+com.xray.transport.internet.finalmask.noiseP\x01Z Date: Sat, 14 Feb 2026 10:41:55 +0800 Subject: [PATCH 32/75] Splice --- proxy/proxy.go | 5 ++++- transport/internet/finalmask/finalmask.go | 1 + transport/internet/finalmask/fragment/conn.go | 8 ++++++++ .../internet/finalmask/header/custom/tcp.go | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 74d96856946b..d824230547d4 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -793,6 +793,9 @@ func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - _, ok4 := iConn.(finalmask.TcpMaskConn) + var ok4 bool + if v, ok := iConn.(finalmask.TcpMaskConn); ok { + ok4 = v.Splice() + } return ok1 || ok2 || ok3 || ok4 } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index cc95cf03907c..24bac394d457 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -93,6 +93,7 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) { type TcpMaskConn interface { TcpMaskConn() RawConn() net.Conn + Splice() bool } func UnwrapTcpMask(conn net.Conn) net.Conn { diff --git a/transport/internet/finalmask/fragment/conn.go b/transport/internet/finalmask/fragment/conn.go index 8d48adc628d9..aef4e7a8dca8 100644 --- a/transport/internet/finalmask/fragment/conn.go +++ b/transport/internet/finalmask/fragment/conn.go @@ -32,6 +32,14 @@ func (c *fragmentConn) RawConn() net.Conn { return c.Conn } +func (c *fragmentConn) Splice() bool { + type Splice interface{ Splice() bool } + if v, ok := c.Conn.(Splice); ok { + return true && v.Splice() + } + return true +} + func (c *fragmentConn) Write(p []byte) (n int, err error) { c.count++ diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 6a6f4ad705be..23f7737076c8 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -61,6 +61,14 @@ func (c *tcpCustomClientConn) RawConn() net.Conn { return c.Conn } +func (c *tcpCustomClientConn) Splice() bool { + type Splice interface{ Splice() bool } + if v, ok := c.Conn.(Splice); ok { + return true && v.Splice() + } + return true +} + func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) { c.wg.Wait() @@ -216,6 +224,14 @@ func (c *tcpCustomServerConn) RawConn() net.Conn { return c.Conn } +func (c *tcpCustomServerConn) Splice() bool { + type Splice interface{ Splice() bool } + if v, ok := c.Conn.(Splice); ok { + return true && v.Splice() + } + return true +} + func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { c.once.Do(func() { var buf [8192]byte From c0b72402b4dd0568024e977688bcf1b6d40f8468 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 10:49:15 +0800 Subject: [PATCH 33/75] spliceable --- proxy/proxy.go | 5 +---- transport/internet/finalmask/finalmask.go | 16 ++++++++++++++++ transport/internet/finalmask/fragment/conn.go | 4 ---- .../internet/finalmask/header/custom/tcp.go | 8 -------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index d824230547d4..246a5be956fe 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -793,9 +793,6 @@ func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - var ok4 bool - if v, ok := iConn.(finalmask.TcpMaskConn); ok { - ok4 = v.Splice() - } + ok4 := finalmask.SpliceAble(iConn) return ok1 || ok2 || ok3 || ok4 } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 24bac394d457..6d1a353c3723 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -96,9 +96,25 @@ type TcpMaskConn interface { Splice() bool } +func SpliceAble(conn net.Conn) bool { + for { + if v, ok := conn.(TcpMaskConn); ok { + if !v.Splice() { + return false + } + conn = v.RawConn() + } else { + return true + } + } +} + func UnwrapTcpMask(conn net.Conn) net.Conn { for { if v, ok := conn.(TcpMaskConn); ok { + if !v.Splice() { + return conn + } conn = v.RawConn() } else { return conn diff --git a/transport/internet/finalmask/fragment/conn.go b/transport/internet/finalmask/fragment/conn.go index aef4e7a8dca8..08e543590b23 100644 --- a/transport/internet/finalmask/fragment/conn.go +++ b/transport/internet/finalmask/fragment/conn.go @@ -33,10 +33,6 @@ func (c *fragmentConn) RawConn() net.Conn { } func (c *fragmentConn) Splice() bool { - type Splice interface{ Splice() bool } - if v, ok := c.Conn.(Splice); ok { - return true && v.Splice() - } return true } diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 23f7737076c8..f5516f2ac097 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -62,10 +62,6 @@ func (c *tcpCustomClientConn) RawConn() net.Conn { } func (c *tcpCustomClientConn) Splice() bool { - type Splice interface{ Splice() bool } - if v, ok := c.Conn.(Splice); ok { - return true && v.Splice() - } return true } @@ -225,10 +221,6 @@ func (c *tcpCustomServerConn) RawConn() net.Conn { } func (c *tcpCustomServerConn) Splice() bool { - type Splice interface{ Splice() bool } - if v, ok := c.Conn.(Splice); ok { - return true && v.Splice() - } return true } From 5cc4ff282972c13c6c5de5a3137866399426dcb1 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 10:51:40 +0800 Subject: [PATCH 34/75] spliceable --- proxy/proxy.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 246a5be956fe..09d9d23db262 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -793,6 +793,9 @@ func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - ok4 := finalmask.SpliceAble(iConn) + var ok4 bool + if _, ok := iConn.(finalmask.TcpMaskConn); ok { + ok4 = finalmask.SpliceAble(iConn) + } return ok1 || ok2 || ok3 || ok4 } From 42b3bfa684849604e2f1828a040f1c84d8ef399a Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 11:28:24 +0800 Subject: [PATCH 35/75] better logic --- proxy/proxy.go | 5 +---- transport/internet/finalmask/finalmask.go | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 09d9d23db262..246a5be956fe 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -793,9 +793,6 @@ func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - var ok4 bool - if _, ok := iConn.(finalmask.TcpMaskConn); ok { - ok4 = finalmask.SpliceAble(iConn) - } + ok4 := finalmask.SpliceAble(iConn) return ok1 || ok2 || ok3 || ok4 } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 6d1a353c3723..74bebf0ea30f 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -97,6 +97,9 @@ type TcpMaskConn interface { } func SpliceAble(conn net.Conn) bool { + if _, ok := conn.(TcpMaskConn); !ok { + return false + } for { if v, ok := conn.(TcpMaskConn); ok { if !v.Splice() { From 432e5c59f7d52faa0c21120fdae0b455887e7ec1 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 12:20:05 +0800 Subject: [PATCH 36/75] fragment server --- transport/internet/finalmask/fragment/config.go | 4 ++-- transport/internet/finalmask/fragment/conn.go | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/transport/internet/finalmask/fragment/config.go b/transport/internet/finalmask/fragment/config.go index df34d08cd90a..165f11ca9d25 100644 --- a/transport/internet/finalmask/fragment/config.go +++ b/transport/internet/finalmask/fragment/config.go @@ -6,9 +6,9 @@ func (c *Config) TCP() { } func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) { - return NewConnClient(c, raw) + return NewConnClient(c, raw, false) } func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) { - return NewConnServer(c, raw) + return NewConnServer(c, raw, true) } diff --git a/transport/internet/finalmask/fragment/conn.go b/transport/internet/finalmask/fragment/conn.go index 08e543590b23..1cadf5f0aef0 100644 --- a/transport/internet/finalmask/fragment/conn.go +++ b/transport/internet/finalmask/fragment/conn.go @@ -11,28 +11,38 @@ type fragmentConn struct { net.Conn config *Config count uint64 + + server bool } -func NewConnClient(c *Config, raw net.Conn) (net.Conn, error) { +func NewConnClient(c *Config, raw net.Conn, server bool) (net.Conn, error) { conn := &fragmentConn{ Conn: raw, config: c, + + server: server, } return conn, nil } -func NewConnServer(c *Config, raw net.Conn) (net.Conn, error) { - return NewConnClient(c, raw) +func NewConnServer(c *Config, raw net.Conn, server bool) (net.Conn, error) { + return NewConnClient(c, raw, server) } func (c *fragmentConn) TcpMaskConn() {} func (c *fragmentConn) RawConn() net.Conn { + if c.server { + return c + } return c.Conn } func (c *fragmentConn) Splice() bool { + if c.server { + return false + } return true } From 0d52f04c1b19de85e94477f132560e718f8d8bbc Mon Sep 17 00:00:00 2001 From: null Date: Sat, 14 Feb 2026 21:56:25 +0800 Subject: [PATCH 37/75] freedom --- infra/conf/freedom.go | 133 +++++++++++++++++++++- infra/conf/transport_internet.go | 10 +- proxy/freedom/freedom.go | 183 ++++++++++++++++++++++++++++++- 3 files changed, 318 insertions(+), 8 deletions(-) diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 62f2b306d16a..82d2c9a8bc2a 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -1,6 +1,8 @@ package conf import ( + "encoding/base64" + "encoding/hex" "net" "strings" @@ -23,6 +25,21 @@ type FreedomConfig struct { ProxyProtocol uint32 `json:"proxyProtocol"` } +type Fragment struct { + Packets string `json:"packets"` + Length *Int32Range `json:"length"` + Interval *Int32Range `json:"interval"` + MaxSplit *Int32Range `json:"maxSplit"` +} + +type Noise struct { + Type string `json:"type"` + Packet string `json:"packet"` + Delay *Int32Range `json:"delay"` + ApplyTo string `json:"applyTo"` +} + +// Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) targetStrategy := c.TargetStrategy @@ -57,15 +74,69 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } if c.Fragment != nil { - return nil, errors.PrintRemovedFeatureError("fragment", "finalmask/tcp fragment") + config.Fragment = new(freedom.Fragment) + + switch strings.ToLower(c.Fragment.Packets) { + case "tlshello": + // TLS Hello Fragmentation (into multiple handshake messages) + config.Fragment.PacketsFrom = 0 + config.Fragment.PacketsTo = 1 + case "": + // TCP Segmentation (all packets) + config.Fragment.PacketsFrom = 0 + config.Fragment.PacketsTo = 0 + default: + // TCP Segmentation (range) + from, to, err := ParseRangeString(c.Fragment.Packets) + if err != nil { + return nil, errors.New("Invalid PacketsFrom").Base(err) + } + config.Fragment.PacketsFrom = uint64(from) + config.Fragment.PacketsTo = uint64(to) + if config.Fragment.PacketsFrom == 0 { + return nil, errors.New("PacketsFrom can't be 0") + } + } + + { + if c.Fragment.Length == nil { + return nil, errors.New("Length can't be empty") + } + config.Fragment.LengthMin = uint64(c.Fragment.Length.From) + config.Fragment.LengthMax = uint64(c.Fragment.Length.To) + if config.Fragment.LengthMin == 0 { + return nil, errors.New("LengthMin can't be 0") + } + } + + { + if c.Fragment.Interval == nil { + return nil, errors.New("Interval can't be empty") + } + config.Fragment.IntervalMin = uint64(c.Fragment.Interval.From) + config.Fragment.IntervalMax = uint64(c.Fragment.Interval.To) + } + + { + if c.Fragment.MaxSplit != nil { + config.Fragment.MaxSplitMin = uint64(c.Fragment.MaxSplit.From) + config.Fragment.MaxSplitMax = uint64(c.Fragment.MaxSplit.To) + } + } } if c.Noise != nil { - return nil, errors.PrintRemovedFeatureError("noise", "finalmask/udp noise") + return nil, errors.PrintRemovedFeatureError("noise = { ... }", "noises = [ { ... } ]") } if c.Noises != nil { - return nil, errors.PrintRemovedFeatureError("noise", "finalmask/udp noise") + for _, n := range c.Noises { + NConfig, err := ParseNoise(n) + if err != nil { + return nil, err + } + config.Noises = append(config.Noises, NConfig) + } } config.UserLevel = c.UserLevel @@ -93,3 +164,59 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } return config, nil } + +func ParseNoise(noise *Noise) (*freedom.Noise, error) { + var err error + NConfig := new(freedom.Noise) + noise.Packet = strings.TrimSpace(noise.Packet) + + switch noise.Type { + case "rand": + min, max, err := ParseRangeString(noise.Packet) + if err != nil { + return nil, errors.New("invalid value for rand Length").Base(err) + } + NConfig.LengthMin = uint64(min) + NConfig.LengthMax = uint64(max) + if NConfig.LengthMin == 0 { + return nil, errors.New("rand lengthMin or lengthMax cannot be 0") + } + + case "str": + // user input string + NConfig.Packet = []byte(noise.Packet) + + case "hex": + // user input hex + NConfig.Packet, err = hex.DecodeString(noise.Packet) + if err != nil { + return nil, errors.New("Invalid hex string").Base(err) + } + + case "base64": + // user input base64 + NConfig.Packet, err = base64.RawURLEncoding.DecodeString(strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(noise.Packet)) + if err != nil { + return nil, errors.New("Invalid base64 string").Base(err) + } + + default: + return nil, errors.New("Invalid packet, only rand/str/hex/base64 are supported") + } + + if noise.Delay != nil { + NConfig.DelayMin = uint64(noise.Delay.From) + NConfig.DelayMax = uint64(noise.Delay.To) + } + switch strings.ToLower(noise.ApplyTo) { + case "", "ip", "all": + NConfig.ApplyTo = "ip" + case "ipv4": + NConfig.ApplyTo = "ipv4" + case "ipv6": + NConfig.ApplyTo = "ipv6" + default: + return nil, errors.New("Invalid applyTo, only ip/ipv4/ipv6 are supported") + } + return NConfig, nil +} diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index ce7160ad6bcf..fda99f1259e6 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1282,6 +1282,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { var ( tcpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ "header-custom": func() interface{} { return new(HeaderCustomTCP) }, + "fragment": func() interface{} { return new(FragmentMask) }, }, "type", "settings") udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ @@ -1294,6 +1295,7 @@ var ( "header-wireguard": func() interface{} { return new(Wireguard) }, "mkcp-original": func() interface{} { return new(Original) }, "mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) }, + "noise": func() interface{} { return new(NoiseMask) }, "salamander": func() interface{} { return new(Salamander) }, "xdns": func() interface{} { return new(Xdns) }, "xicmp": func() interface{} { return new(Xicmp) }, @@ -1370,14 +1372,14 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { }, nil } -type Fragment struct { +type FragmentMask struct { Packets string `json:"packets"` Length Int32Range `json:"length"` Delay Int32Range `json:"delay"` MaxSplit Int32Range `json:"maxSplit"` } -func (c *Fragment) Build() (proto.Message, error) { +func (c *FragmentMask) Build() (proto.Message, error) { config := &fragment.Config{} switch strings.ToLower(c.Packets) { @@ -1420,12 +1422,12 @@ type NoiseItem struct { Delay Int32Range `json:"delay"` } -type Noise struct { +type NoiseMask struct { Reset Int32Range `json:"reset"` Noise []NoiseItem `json:"noise"` } -func (c *Noise) Build() (proto.Message, error) { +func (c *NoiseMask) Build() (proto.Message, error) { for _, item := range c.Noise { if len(item.Packet) > 0 && item.Rand.To > 0 { return nil, errors.New("len(item.Packet) > 0 && item.Rand.To > 0") diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index f0247aa39d84..d107e357157b 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -2,10 +2,14 @@ package freedom import ( "context" + "crypto/rand" + "io" + "time" "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" @@ -174,9 +178,28 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var writer buf.Writer if destination.Network == net.Network_TCP { - writer = buf.NewWriter(conn) + if h.config.Fragment != nil { + errors.LogDebug(ctx, "FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax, + h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax, h.config.Fragment.MaxSplitMin, h.config.Fragment.MaxSplitMax) + writer = buf.NewWriter(&FragmentWriter{ + fragment: h.config.Fragment, + writer: conn, + }) + } else { + writer = buf.NewWriter(conn) + } } else { writer = NewPacketWriter(conn, h, UDPOverride, destination) + if h.config.Noises != nil { + errors.LogDebug(ctx, "NOISE", h.config.Noises) + writer = &NoisePacketWriter{ + Writer: writer, + noises: h.config.Noises, + firstWrite: true, + UDPOverride: UDPOverride, + remoteAddr: net.DestinationFromAddr(conn.RemoteAddr()).Address, + } + } } if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { @@ -396,3 +419,161 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } return nil } + +type NoisePacketWriter struct { + buf.Writer + noises []*Noise + firstWrite bool + UDPOverride net.Destination + remoteAddr net.Address +} + +// MultiBuffer writer with Noise before first packet +func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { + if w.firstWrite { + w.firstWrite = false + //Do not send Noise for dns requests(just to be safe) + if w.UDPOverride.Port == 53 { + return w.Writer.WriteMultiBuffer(mb) + } + var noise []byte + var err error + if w.remoteAddr.Family().IsDomain() { + panic("impossible, remoteAddr is always IP") + } + for _, n := range w.noises { + switch n.ApplyTo { + case "ipv4": + if w.remoteAddr.Family().IsIPv6() { + continue + } + case "ipv6": + if w.remoteAddr.Family().IsIPv4() { + continue + } + case "ip": + default: + panic("unreachable, applyTo is ip/ipv4/ipv6") + } + //User input string or base64 encoded string or hex string + if n.Packet != nil { + noise = n.Packet + } else { + //Random noise + noise, err = GenerateRandomBytes(crypto.RandBetween(int64(n.LengthMin), + int64(n.LengthMax))) + } + if err != nil { + return err + } + err = w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) + if err != nil { + return err + } + + if n.DelayMin != 0 || n.DelayMax != 0 { + time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond) + } + } + + } + return w.Writer.WriteMultiBuffer(mb) +} + +type FragmentWriter struct { + fragment *Fragment + writer io.Writer + count uint64 +} + +func (f *FragmentWriter) Write(b []byte) (int, error) { + f.count++ + + if f.fragment.PacketsFrom == 0 && f.fragment.PacketsTo == 1 { + if f.count != 1 || len(b) <= 5 || b[0] != 22 { + return f.writer.Write(b) + } + recordLen := 5 + ((int(b[3]) << 8) | int(b[4])) + if len(b) < recordLen { // maybe already fragmented somehow + return f.writer.Write(b) + } + data := b[5:recordLen] + buff := make([]byte, 2048) + var hello []byte + maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax)) + var splitNum int64 + for from := 0; ; { + to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) + splitNum++ + if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) { + to = len(data) + } + l := to - from + if 5+l > len(buff) { + buff = make([]byte, 5+l) + } + copy(buff[:3], b) + copy(buff[5:], data[from:to]) + from = to + buff[3] = byte(l >> 8) + buff[4] = byte(l) + if f.fragment.IntervalMax == 0 { // combine fragmented tlshello if interval is 0 + hello = append(hello, buff[:5+l]...) + } else { + _, err := f.writer.Write(buff[:5+l]) + time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) + if err != nil { + return 0, err + } + } + if from == len(data) { + if len(hello) > 0 { + _, err := f.writer.Write(hello) + if err != nil { + return 0, err + } + } + if len(b) > recordLen { + n, err := f.writer.Write(b[recordLen:]) + if err != nil { + return recordLen + n, err + } + } + return len(b), nil + } + } + } + + if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) { + return f.writer.Write(b) + } + maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax)) + var splitNum int64 + for from := 0; ; { + to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) + splitNum++ + if to > len(b) || (maxSplit > 0 && splitNum >= maxSplit) { + to = len(b) + } + n, err := f.writer.Write(b[from:to]) + from += n + if err != nil { + return from, err + } + time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) + if from >= len(b) { + return from, nil + } + } +} + +func GenerateRandomBytes(n int64) ([]byte, error) { + b := make([]byte, n) + _, err := rand.Read(b) + // Note that err == nil only if we read len(b) bytes. + if err != nil { + return nil, err + } + + return b, nil +} From 83470462719334a1fbf1d58a5b43480ee3ae7a2b Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 14:09:15 +0800 Subject: [PATCH 38/75] net.Error & net.ErrClosed & packet.addr --- .../internet/finalmask/header/custom/udp.go | 35 +++++-------- .../internet/finalmask/header/dns/conn.go | 48 +++++------------- .../internet/finalmask/header/dtls/conn.go | 48 +++++------------- .../internet/finalmask/header/srtp/conn.go | 46 +++++------------ .../internet/finalmask/header/utp/conn.go | 48 +++++------------- .../internet/finalmask/header/wechat/conn.go | 48 +++++------------- .../finalmask/header/wireguard/conn.go | 50 ++++++------------- .../internet/finalmask/mkcp/aes128gcm/conn.go | 50 ++++++------------- .../internet/finalmask/mkcp/original/conn.go | 50 ++++++------------- .../internet/finalmask/salamander/conn.go | 48 +++++------------- transport/internet/finalmask/xdns/client.go | 4 +- transport/internet/finalmask/xdns/server.go | 4 +- transport/internet/finalmask/xicmp/client.go | 4 +- transport/internet/finalmask/xicmp/server.go | 4 +- 14 files changed, 139 insertions(+), 348 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 6704626e58ef..8b3eaa813c56 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -4,9 +4,10 @@ import ( "bytes" "context" "crypto/rand" + go_errors "errors" "io" "net" - sync "sync" + "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" @@ -34,7 +35,6 @@ func (h *udpCustomClient) Serialize(b []byte) { type udpCustomClientConn struct { first bool leaveSize int32 - closed bool net.PacketConn header *udpCustomClient @@ -85,13 +85,13 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -184,11 +184,6 @@ func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error return c.PacketConn.WriteTo(p, addr) } -func (c *udpCustomClientConn) Close() error { - c.closed = true - return c.PacketConn.Close() -} - type udpCustomServer struct { client []*UDPItem server []*UDPItem @@ -211,7 +206,6 @@ func (h *udpCustomServer) Serialize(b []byte) { type udpCustomServerConn struct { first bool leaveSize int32 - closed bool net.PacketConn header *udpCustomServer @@ -262,13 +256,13 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -360,8 +354,3 @@ func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error return c.PacketConn.WriteTo(p, addr) } - -func (c *udpCustomServerConn) Close() error { - c.closed = true - return c.PacketConn.Close() -} diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index b26a42be8b9b..c8c7c932d178 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -3,10 +3,10 @@ package dns import ( "context" "encoding/binary" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -94,9 +94,8 @@ func (h *dns) Serialize(b []byte) { type dnsConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *dns readBuf []byte @@ -126,7 +125,7 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, + PacketConn: raw, header: &dns{ header: header, }, @@ -153,13 +152,13 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -181,7 +180,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -208,7 +207,7 @@ func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -226,26 +225,5 @@ func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *dnsConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 468f0805cf3a..572fdd508c94 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -2,10 +2,10 @@ package dtls import ( "context" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -45,9 +45,8 @@ func (h *dtls) Serialize(b []byte) { type dtlsConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *dtls readBuf []byte @@ -61,7 +60,7 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, + PacketConn: raw, header: &dtls{ epoch: dice.RollUint16(), sequence: 0, @@ -90,13 +89,13 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -118,7 +117,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -145,7 +144,7 @@ func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -163,26 +162,5 @@ func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *dtlsConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index fae99da8e2d6..5f2fa296a5cd 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -3,10 +3,10 @@ package srtp import ( "context" "encoding/binary" + go_errors "errors" "io" "net" sync "sync" - "time" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -30,9 +30,8 @@ func (h *srtp) Serialize(b []byte) { type srtpConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *srtp readBuf []byte @@ -46,7 +45,7 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, + PacketConn: raw, header: &srtp{ header: 0xB5E8, number: dice.RollUint16(), @@ -74,13 +73,13 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -102,7 +101,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -129,7 +128,7 @@ func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -147,26 +146,5 @@ func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *srtpConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 8c0ced3fe031..18f3c887d001 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -3,10 +3,10 @@ package utp import ( "context" "encoding/binary" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -31,9 +31,8 @@ func (h *utp) Serialize(b []byte) { type utpConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *utp readBuf []byte @@ -47,7 +46,7 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, + PacketConn: raw, header: &utp{ header: 1, extension: 0, @@ -76,13 +75,13 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -104,7 +103,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -131,7 +130,7 @@ func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -149,26 +148,5 @@ func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *utpConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index fc42f034f4fc..164d7916654a 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -3,10 +3,10 @@ package wechat import ( "context" "encoding/binary" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -37,9 +37,8 @@ func (h *wechat) Serialize(b []byte) { type wechatConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *wechat readBuf []byte @@ -53,7 +52,7 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, + PacketConn: raw, header: &wechat{ sn: uint32(dice.RollUint16()), }, @@ -80,13 +79,13 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -108,7 +107,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -135,7 +134,7 @@ func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -153,26 +152,5 @@ func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *wechatConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 4ff0940a76b0..d9029d12946f 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -2,10 +2,10 @@ package wireguard import ( "context" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common/errors" ) @@ -26,9 +26,8 @@ func (h *wireguare) Serialize(b []byte) { type wireguareConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn header *wireguare readBuf []byte @@ -42,8 +41,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, - header: &wireguare{}, + PacketConn: raw, + header: &wireguare{}, } if first { @@ -67,13 +66,13 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -95,7 +94,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -122,7 +121,7 @@ func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -140,26 +139,5 @@ func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - return c.conn.WriteTo(p, addr) -} - -func (c *wireguareConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 16517b2ee20c..d1d94cc1f93e 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -5,10 +5,10 @@ import ( "crypto/cipher" "crypto/rand" "crypto/sha256" + go_errors "errors" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" @@ -18,9 +18,8 @@ import ( type aes128gcmConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn aead cipher.AEAD readBuf []byte @@ -36,8 +35,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, - aead: crypto.NewAesGcm(hashedPsk[:16]), + PacketConn: raw, + aead: crypto.NewAesGcm(hashedPsk[:16]), } if first { @@ -61,13 +60,13 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -96,7 +95,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -136,7 +135,7 @@ func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { 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) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -159,26 +158,5 @@ func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { 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 { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index c7841a1065d8..02d4978a19b3 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -4,11 +4,11 @@ import ( "context" "crypto/cipher" "encoding/binary" + go_errors "errors" "hash/fnv" "io" "net" - sync "sync" - "time" + "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" @@ -78,9 +78,8 @@ func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) { type simpleConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn aead cipher.AEAD readBuf []byte @@ -94,8 +93,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, - aead: &simple{}, + PacketConn: raw, + aead: &simple{}, } if first { @@ -119,13 +118,13 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -154,7 +153,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -190,7 +189,7 @@ func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { sealed := c.aead.Seal(nil, nil, plaintext, nil) copy(c.writeBuf[c.leaveSize:], sealed) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -210,26 +209,5 @@ func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { sealed := c.aead.Seal(nil, nil, plaintext, nil) copy(p[c.leaveSize:], sealed) - return c.conn.WriteTo(p, addr) -} - -func (c *simpleConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index a1e7ece5fee8..6efafa2d41ad 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -2,10 +2,10 @@ package salamander import ( "context" + go_errors "errors" "io" "net" "sync" - "time" "github.com/xtls/xray-core/common/errors" ) @@ -13,9 +13,8 @@ import ( type obfsPacketConn struct { first bool leaveSize int32 - closed bool - conn net.PacketConn + net.PacketConn obfs *SalamanderObfuscator readBuf []byte @@ -34,8 +33,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( first: first, leaveSize: leaveSize, - conn: raw, - obfs: ob, + PacketConn: raw, + obfs: ob, } if first { @@ -59,13 +58,13 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.readMutex.Lock() for { - if c.closed { - c.readMutex.Unlock() - return 0, nil, io.EOF - } - - n, addr, err = c.conn.ReadFrom(c.readBuf) + n, addr, err = c.PacketConn.ReadFrom(c.readBuf) if err != nil { + var ne net.Error + if go_errors.As(err, &ne) { + c.readMutex.Unlock() + return n, addr, err + } errors.LogDebug(context.Background(), addr, " mask read err ", err) continue } @@ -87,7 +86,7 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } - n, addr, err = c.conn.ReadFrom(p) + n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { return n, addr, err } @@ -114,7 +113,7 @@ func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n]) - nn, err := c.conn.WriteTo(c.writeBuf[:n], addr) + nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) if err != nil { c.writeMutex.Unlock() @@ -132,26 +131,5 @@ func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:]) - return c.conn.WriteTo(p, addr) -} - -func (c *obfsPacketConn) Close() error { - c.closed = true - 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) + return c.PacketConn.WriteTo(p, addr) } diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 70e375768f3b..ad69a3bdce59 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -190,11 +190,11 @@ func (c *xdnsConnClient) Size() int32 { func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } n = copy(p, packet.p) if n != len(packet.p) { - return 0, addr, io.ErrShortBuffer + return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 817b709471f9..37adad5060a9 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -315,11 +315,11 @@ func (c *xdnsConnServer) Size() int32 { func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } n = copy(p, packet.p) if n != len(packet.p) { - return 0, addr, io.ErrShortBuffer + return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 780d04e9ba14..d6b7b2138246 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -286,11 +286,11 @@ func (c *xicmpConnClient) Size() int32 { func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } n = copy(p, packet.p) if n != len(packet.p) { - return 0, addr, io.ErrShortBuffer + return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 4ab5ebad142b..831e59499a3c 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -309,11 +309,11 @@ func (c *xicmpConnServer) Size() int32 { func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } n = copy(p, packet.p) if n != len(packet.p) { - return 0, addr, io.ErrShortBuffer + return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil } From 1f5490beb24ddbeeccc6b00a31288a8db3eb97b8 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 14:09:53 +0800 Subject: [PATCH 39/75] 1 --- infra/conf/transport_internet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index fda99f1259e6..9fb5def9a151 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1321,7 +1321,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { return nil, errors.New("len > 8192") } if len(item.Packet) > 0 && item.Rand > 0 { - return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") } } } @@ -1331,7 +1331,7 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { return nil, errors.New("len > 8192") } if len(item.Packet) > 0 && item.Rand > 0 { - return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") } } } From c5864f69bd8fe6e71ec6fedb36ea717e2a490ce6 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 14:15:13 +0800 Subject: [PATCH 40/75] io.ErrShortBuffer --- transport/internet/finalmask/header/custom/udp.go | 12 ++++++------ transport/internet/finalmask/header/dns/conn.go | 6 +++--- transport/internet/finalmask/header/dtls/conn.go | 6 +++--- transport/internet/finalmask/header/srtp/conn.go | 6 +++--- transport/internet/finalmask/header/utp/conn.go | 6 +++--- transport/internet/finalmask/header/wechat/conn.go | 6 +++--- .../internet/finalmask/header/wireguard/conn.go | 6 +++--- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 8b3eaa813c56..4eef1d8a9cd0 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -116,13 +116,13 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro continue } + copy(p, c.readBuf[index:n]) + if len(p) < n-index { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[index:n]) - c.readMutex.Unlock() return n - index, addr, nil } @@ -287,13 +287,13 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro continue } + copy(p, c.readBuf[index:n]) + if len(p) < n-index { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[index:n]) - c.readMutex.Unlock() return n - index, addr, nil } diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index c8c7c932d178..af2b44df1d13 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -168,13 +168,13 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 572fdd508c94..37a5b40bb3d3 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -105,13 +105,13 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 5f2fa296a5cd..023b740ad65e 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -89,13 +89,13 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 18f3c887d001..5602585331e0 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -91,13 +91,13 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index 164d7916654a..7ff2b16144d1 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -95,13 +95,13 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index d9029d12946f..8616e236878c 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -82,13 +82,13 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { continue } + copy(p, c.readBuf[c.Size():n]) + if len(p) < n-int(c.Size()) { c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + return len(p), addr, io.ErrShortBuffer } - copy(p, c.readBuf[c.Size():n]) - c.readMutex.Unlock() return n - int(c.Size()), addr, nil } From fc9842ea6e9b365c094fa89391079512948c1e75 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 14:16:20 +0800 Subject: [PATCH 41/75] sync --- transport/internet/finalmask/header/srtp/conn.go | 2 +- transport/internet/finalmask/noise/conn.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 023b740ad65e..867d2f306cc3 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -6,7 +6,7 @@ import ( go_errors "errors" "io" "net" - sync "sync" + "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go index 324a13f30b01..b37a2bf77326 100644 --- a/transport/internet/finalmask/noise/conn.go +++ b/transport/internet/finalmask/noise/conn.go @@ -3,7 +3,7 @@ package noise import ( "crypto/rand" "net" - sync "sync" + "sync" "time" "github.com/xtls/xray-core/common" From af6f2b69f80b9234348584535e53169bb8a3f463 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 16:47:18 +0800 Subject: [PATCH 42/75] logdebug io.ErrShortBuffer --- transport/internet/finalmask/header/custom/udp.go | 9 ++++----- transport/internet/finalmask/header/dns/conn.go | 5 ++--- transport/internet/finalmask/header/dtls/conn.go | 5 ++--- transport/internet/finalmask/header/srtp/conn.go | 5 ++--- transport/internet/finalmask/header/utp/conn.go | 5 ++--- transport/internet/finalmask/header/wechat/conn.go | 5 ++--- transport/internet/finalmask/header/wireguard/conn.go | 5 ++--- transport/internet/finalmask/mkcp/aes128gcm/conn.go | 5 ++--- transport/internet/finalmask/mkcp/original/conn.go | 5 ++--- transport/internet/finalmask/salamander/conn.go | 5 ++--- transport/internet/finalmask/xdns/client.go | 1 + transport/internet/finalmask/xdns/server.go | 1 + transport/internet/finalmask/xicmp/client.go | 1 + transport/internet/finalmask/xicmp/server.go | 1 + 14 files changed, 26 insertions(+), 32 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 4eef1d8a9cd0..8173beb50e95 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -5,7 +5,6 @@ import ( "context" "crypto/rand" go_errors "errors" - "io" "net" "sync" @@ -119,8 +118,8 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro copy(p, c.readBuf[index:n]) if len(p) < n-index { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-index) + continue } c.readMutex.Unlock() @@ -290,8 +289,8 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro copy(p, c.readBuf[index:n]) if len(p) < n-index { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-index) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index af2b44df1d13..19cee789404a 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" go_errors "errors" - "io" "net" "sync" @@ -171,8 +170,8 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 37a5b40bb3d3..ed8372c2f60b 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -3,7 +3,6 @@ package dtls import ( "context" go_errors "errors" - "io" "net" "sync" @@ -108,8 +107,8 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 867d2f306cc3..7fe3619978be 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" go_errors "errors" - "io" "net" "sync" @@ -92,8 +91,8 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 5602585331e0..d10afbb08f10 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" go_errors "errors" - "io" "net" "sync" @@ -94,8 +93,8 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index 7ff2b16144d1..ae007b0f6da6 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" go_errors "errors" - "io" "net" "sync" @@ -98,8 +97,8 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 8616e236878c..ad31a73a600e 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -3,7 +3,6 @@ package wireguard import ( "context" go_errors "errors" - "io" "net" "sync" @@ -85,8 +84,8 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { copy(p, c.readBuf[c.Size():n]) if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return len(p), addr, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.readMutex.Unlock() diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index d1d94cc1f93e..7079ccecdab7 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "crypto/sha256" go_errors "errors" - "io" "net" "sync" @@ -77,8 +76,8 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } nonceSize := c.aead.NonceSize() diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index 02d4978a19b3..0f70f0b8a0a3 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -6,7 +6,6 @@ import ( "encoding/binary" go_errors "errors" "hash/fnv" - "io" "net" "sync" @@ -135,8 +134,8 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } ciphertext := c.readBuf[:n] diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 6efafa2d41ad..297956adca1a 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -3,7 +3,6 @@ package salamander import ( "context" go_errors "errors" - "io" "net" "sync" @@ -75,8 +74,8 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } if len(p) < n-int(c.Size()) { - c.readMutex.Unlock() - return 0, nil, io.ErrShortBuffer + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) + continue } c.obfs.Deobfuscate(c.readBuf[:n], p) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index ad69a3bdce59..8f1a37a7480a 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -194,6 +194,7 @@ func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 37adad5060a9..3152e0ab345b 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -319,6 +319,7 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index d6b7b2138246..7f927413572e 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -290,6 +290,7 @@ func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 831e59499a3c..9fa0c461f22e 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -313,6 +313,7 @@ func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } n = copy(p, packet.p) if n != len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) return n, packet.addr, io.ErrShortBuffer } return n, packet.addr, nil From 85c9f56ff5bf932fc464d1c7cbd2c21d2588bca0 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 16:51:47 +0800 Subject: [PATCH 43/75] finalmask.UDPSize --- transport/internet/finalmask/finalmask.go | 4 ++++ transport/internet/finalmask/header/custom/udp.go | 13 +++++++------ transport/internet/finalmask/header/dns/conn.go | 7 ++++--- transport/internet/finalmask/header/dtls/conn.go | 7 ++++--- transport/internet/finalmask/header/srtp/conn.go | 7 ++++--- transport/internet/finalmask/header/utp/conn.go | 7 ++++--- transport/internet/finalmask/header/wechat/conn.go | 7 ++++--- .../internet/finalmask/header/wireguard/conn.go | 7 ++++--- transport/internet/finalmask/mkcp/aes128gcm/conn.go | 7 ++++--- transport/internet/finalmask/mkcp/original/conn.go | 7 ++++--- transport/internet/finalmask/salamander/conn.go | 7 ++++--- transport/internet/finalmask/xdns/client.go | 3 ++- transport/internet/finalmask/xdns/server.go | 3 ++- transport/internet/finalmask/xicmp/client.go | 7 ++++--- 14 files changed, 55 insertions(+), 38 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 74bebf0ea30f..335ab1af1f7e 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -4,6 +4,10 @@ import ( "net" ) +const ( + UDPSize = 4096 +) + type ConnSize interface { Size() int32 } diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 8173beb50e95..037daf49e1d8 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -10,6 +10,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type udpCustomClient struct { @@ -57,8 +58,8 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } index := 0 @@ -151,7 +152,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } @@ -228,8 +229,8 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } index := 0 @@ -322,7 +323,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > 8192 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index 19cee789404a..684ae68f85f6 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -9,6 +9,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) func packDomainName(s string, msg []byte) (off1 int, err error) { @@ -131,8 +132,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -195,7 +196,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index ed8372c2f60b..f3508dc9a2c7 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -8,6 +8,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type dtls struct { @@ -68,8 +69,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -132,7 +133,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 7fe3619978be..dd2e78cf1810 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -9,6 +9,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type srtp struct { @@ -52,8 +53,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -116,7 +117,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index d10afbb08f10..63ff768e258a 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -9,6 +9,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type utp struct { @@ -54,8 +55,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -118,7 +119,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index ae007b0f6da6..b9a82dc1c57a 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -9,6 +9,7 @@ import ( "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type wechat struct { @@ -58,8 +59,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -122,7 +123,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index ad31a73a600e..f83d689cddde 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type wireguare struct{} @@ -45,8 +46,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -109,7 +110,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 7079ccecdab7..1fd7bf76cb5e 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -12,6 +12,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type aes128gcmConn struct { @@ -39,8 +40,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -117,7 +118,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index 0f70f0b8a0a3..563ef5105ab0 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -11,6 +11,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type simple struct{} @@ -97,8 +98,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -175,7 +176,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 297956adca1a..91feb1e687ab 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) type obfsPacketConn struct { @@ -37,8 +38,8 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( } if first { - conn.readBuf = make([]byte, 8192) - conn.writeBuf = make([]byte, 8192) + conn.readBuf = make([]byte, finalmask.UDPSize) + conn.writeBuf = make([]byte, finalmask.UDPSize) } return conn, nil @@ -101,7 +102,7 @@ func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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 { + if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { return 0, errors.New("too many masks") } diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 8f1a37a7480a..a7e24f5d7944 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -12,6 +12,7 @@ import ( "time" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) const ( @@ -79,7 +80,7 @@ func (c *xdnsConnClient) recvLoop() { break } - var buf [4096]byte + var buf [finalmask.UDPSize]byte n, addr, err := c.conn.ReadFrom(buf[:]) if err != nil { diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 3152e0ab345b..1c629a905255 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -10,6 +10,7 @@ import ( "time" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" ) const ( @@ -158,7 +159,7 @@ func (c *xdnsConnServer) recvLoop() { break } - var buf [4096]byte + var buf [finalmask.UDPSize]byte n, addr, err := c.conn.ReadFrom(buf[:]) if err != nil { continue diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 7f927413572e..221c93ac8861 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -11,6 +11,7 @@ import ( "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/hysteria/udphop" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" @@ -126,8 +127,8 @@ func (c *xicmpConnClient) encode(p []byte) ([]byte, error) { return nil, err } - if len(buf) > 8192 { - return nil, errors.New("xicmp len(buf) > 8192") + if len(buf) > finalmask.UDPSize { + return nil, errors.New("xicmp len(buf) > finalmask.UDPSize") } c.seqStatus[c.seq] = &seqStatus{ @@ -153,7 +154,7 @@ func (c *xicmpConnClient) recvLoop() { break } - var buf [8192]byte + var buf [finalmask.UDPSize]byte n, addr, err := c.icmpConn.ReadFrom(buf[:]) if err != nil { From 7cd7cce7e82bbd32b4bbb27822162bb1b31cff9a Mon Sep 17 00:00:00 2001 From: null Date: Sun, 15 Feb 2026 16:59:30 +0800 Subject: [PATCH 44/75] refactor xdns --- transport/internet/finalmask/xdns/client.go | 56 ++++++------------ transport/internet/finalmask/xdns/server.go | 63 +++++++-------------- 2 files changed, 36 insertions(+), 83 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index a7e24f5d7944..199c67beb8ac 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "encoding/base32" "encoding/binary" + go_errors "errors" "io" "net" "sync" @@ -32,7 +33,7 @@ type packet struct { } type xdnsConnClient struct { - conn net.PacketConn + net.PacketConn clientID []byte domain Name @@ -56,7 +57,7 @@ func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err } conn := &xdnsConnClient{ - conn: raw, + PacketConn: raw, clientID: make([]byte, 8), domain: domain, @@ -75,15 +76,14 @@ func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err } func (c *xdnsConnClient) recvLoop() { - for { - if c.closed { - break - } + var buf [finalmask.UDPSize]byte - var buf [finalmask.UDPSize]byte - - n, addr, err := c.conn.ReadFrom(buf[:]) + for { + n, addr, err := c.PacketConn.ReadFrom(buf[:]) if err != nil { + if go_errors.Is(err, net.ErrClosed) { + break + } continue } @@ -124,6 +124,12 @@ func (c *xdnsConnClient) recvLoop() { close(c.pollChan) close(c.readQueue) + + c.mutex.Lock() + defer c.mutex.Unlock() + + c.closed = true + close(c.writeQueue) } func (c *xdnsConnClient) sendLoop() { @@ -179,7 +185,7 @@ func (c *xdnsConnClient) sendLoop() { } if p != nil { - _, _ = c.conn.WriteTo(p.p, p.addr) + _, _ = c.PacketConn.WriteTo(p.p, p.addr) } } } @@ -226,36 +232,6 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { } } -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 { diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 1c629a905255..b50e42193ffb 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/binary" + go_errors "errors" "io" "net" "sync" @@ -49,7 +50,7 @@ type queue struct { } type xdnsConnServer struct { - conn net.PacketConn + net.PacketConn domain Name @@ -72,7 +73,7 @@ func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err } conn := &xdnsConnServer{ - conn: raw, + PacketConn: raw, domain: domain, @@ -154,14 +155,14 @@ func (c *xdnsConnServer) stash(queue *queue, p []byte) { } func (c *xdnsConnServer) recvLoop() { - for { - if c.closed { - break - } + var buf [finalmask.UDPSize]byte - var buf [finalmask.UDPSize]byte - n, addr, err := c.conn.ReadFrom(buf[:]) + for { + n, addr, err := c.PacketConn.ReadFrom(buf[:]) if err != nil { + if go_errors.Is(err, net.ErrClosed) { + break + } continue } @@ -209,6 +210,16 @@ func (c *xdnsConnServer) recvLoop() { close(c.ch) close(c.readQueue) + + c.mutex.Lock() + defer c.mutex.Unlock() + + c.closed = true + for key, q := range c.writeQueueMap { + close(q.queue) + close(q.stash) + delete(c.writeQueueMap, key) + } } func (c *xdnsConnServer) sendLoop() { @@ -305,7 +316,7 @@ func (c *xdnsConnServer) sendLoop() { return } - _, _ = c.conn.WriteTo(buf, rec.Addr) + _, _ = c.PacketConn.WriteTo(buf, rec.Addr) } } @@ -350,40 +361,6 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { } } -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 { From 0205fc1405a50b73950eab103cdd87d636575d73 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 16 Feb 2026 15:30:07 +0800 Subject: [PATCH 45/75] refine spliceable --- transport/internet/finalmask/finalmask.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 335ab1af1f7e..8838e9506cf3 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -101,19 +101,8 @@ type TcpMaskConn interface { } func SpliceAble(conn net.Conn) bool { - if _, ok := conn.(TcpMaskConn); !ok { - return false - } - for { - if v, ok := conn.(TcpMaskConn); ok { - if !v.Splice() { - return false - } - conn = v.RawConn() - } else { - return true - } - } + _, ok := UnwrapTcpMask(conn).(*net.TCPConn) + return ok } func UnwrapTcpMask(conn net.Conn) net.Conn { From 0596bf9d7d237100b91478c5c596102a3153072b Mon Sep 17 00:00:00 2001 From: null Date: Wed, 18 Feb 2026 18:59:30 +0800 Subject: [PATCH 46/75] packet type & tcpCustomServerConn sendError --- infra/conf/transport_internet.go | 102 +++++++++++++++--- .../finalmask/header/custom/config.pb.go | 27 ++--- .../finalmask/header/custom/config.proto | 2 +- .../internet/finalmask/header/custom/tcp.go | 37 +++++-- 4 files changed, 134 insertions(+), 34 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 9fb5def9a151..65533ddfacc3 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1279,6 +1279,37 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { }, nil } +func PraseByteSlice(data json.RawMessage, typ string) ([]byte, error) { + switch strings.ToLower(typ) { + case "", "array": + var packet []byte + if err := json.Unmarshal(data, &packet); err != nil { + return nil, err + } + return packet, nil + case "str": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return []byte(str), nil + case "hex": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return hex.DecodeString(str) + case "base64": + var str string + if err := json.Unmarshal(data, &str); err != nil { + return nil, err + } + return base64.StdEncoding.DecodeString(str) + default: + return nil, errors.New("unknown type") + } +} + var ( tcpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{ "header-custom": func() interface{} { return new(HeaderCustomTCP) }, @@ -1303,15 +1334,16 @@ var ( ) type TCPItem struct { - Delay Int32Range `json:"delay"` - Rand int32 `json:"rand"` - Packet []byte `json:"packet"` + Delay Int32Range `json:"delay"` + Rand int32 `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` } type HeaderCustomTCP struct { Clients [][]TCPItem `json:"clients"` Servers [][]TCPItem `json:"servers"` - OnError []byte `json:"onError"` + Errors [][]TCPItem `json:"errors"` } func (c *HeaderCustomTCP) Build() (proto.Message, error) { @@ -1335,14 +1367,25 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { } } } - if len(c.OnError) > 8192 { - return nil, errors.New("len > 8192") + for _, value := range c.Errors { + for _, item := range value { + if len(item.Packet) > 8192 || item.Rand > 8192 { + return nil, errors.New("len > 8192") + } + if len(item.Packet) > 0 && item.Rand > 0 { + return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") + } + } } clients := make([]*custom.TCPSequence, len(c.Clients)) for i, value := range c.Clients { clients[i] = &custom.TCPSequence{} for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } clients[i].Sequence = append(clients[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), @@ -1356,6 +1399,10 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { for i, value := range c.Servers { servers[i] = &custom.TCPSequence{} for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } servers[i].Sequence = append(servers[i].Sequence, &custom.TCPItem{ DelayMin: int64(item.Delay.From), DelayMax: int64(item.Delay.To), @@ -1365,10 +1412,27 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { } } + errors := make([]*custom.TCPSequence, len(c.Errors)) + for i, value := range c.Errors { + errors[i] = &custom.TCPSequence{} + for _, item := range value { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } + errors[i].Sequence = append(errors[i].Sequence, &custom.TCPItem{ + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), + Rand: item.Rand, + Packet: item.Packet, + }) + } + } + return &custom.TCPConfig{ Clients: clients, Servers: servers, - OnError: c.OnError, + Errors: errors, }, nil } @@ -1417,9 +1481,10 @@ func (c *FragmentMask) Build() (proto.Message, error) { } type NoiseItem struct { - Rand Int32Range `json:"rand"` - Packet []byte `json:"packet"` - Delay Int32Range `json:"delay"` + Rand Int32Range `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` + Delay Int32Range `json:"delay"` } type NoiseMask struct { @@ -1436,6 +1501,10 @@ func (c *NoiseMask) Build() (proto.Message, error) { noiseSlice := make([]*noise.Item, 0, len(c.Noise)) for _, item := range c.Noise { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } noiseSlice = append(noiseSlice, &noise.Item{ RandMin: int64(item.Rand.From), RandMax: int64(item.Rand.To), @@ -1453,8 +1522,9 @@ func (c *NoiseMask) Build() (proto.Message, error) { } type UDPItem struct { - Rand int32 `json:"rand"` - Packet []byte `json:"packet"` + Rand int32 `json:"rand"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` } type HeaderCustomUDP struct { @@ -1476,6 +1546,10 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) { client := make([]*custom.UDPItem, 0, len(c.Client)) for _, item := range c.Client { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } client = append(client, &custom.UDPItem{ Rand: item.Rand, Packet: item.Packet, @@ -1484,6 +1558,10 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) { server := make([]*custom.UDPItem, 0, len(c.Server)) for _, item := range c.Server { + var err error + if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { + return nil, err + } server = append(server, &custom.UDPItem{ Rand: item.Rand, Packet: item.Packet, diff --git a/transport/internet/finalmask/header/custom/config.pb.go b/transport/internet/finalmask/header/custom/config.pb.go index 7c372e323c50..ff06eb29143d 100644 --- a/transport/internet/finalmask/header/custom/config.pb.go +++ b/transport/internet/finalmask/header/custom/config.pb.go @@ -137,7 +137,7 @@ type TCPConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Clients []*TCPSequence `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` Servers []*TCPSequence `protobuf:"bytes,2,rep,name=servers,proto3" json:"servers,omitempty"` - OnError []byte `protobuf:"bytes,3,opt,name=on_error,json=onError,proto3" json:"on_error,omitempty"` + Errors []*TCPSequence `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -186,9 +186,9 @@ func (x *TCPConfig) GetServers() []*TCPSequence { return nil } -func (x *TCPConfig) GetOnError() []byte { +func (x *TCPConfig) GetErrors() []*TCPSequence { if x != nil { - return x.OnError + return x.Errors } return nil } @@ -308,11 +308,11 @@ const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" "\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" + "\x06packet\x18\x04 \x01(\fR\x06packet\"c\n" + "\vTCPSequence\x12T\n" + - "\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\xd6\x01\n" + + "\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\x91\x02\n" + "\tTCPConfig\x12V\n" + "\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" + - "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12\x19\n" + - "\bon_error\x18\x03 \x01(\fR\aonError\"5\n" + + "\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12T\n" + + "\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"5\n" + "\aUDPItem\x12\x12\n" + "\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x16\n" + "\x06packet\x18\x02 \x01(\fR\x06packet\"\xaf\x01\n" + @@ -345,13 +345,14 @@ var file_transport_internet_finalmask_header_custom_config_proto_depIdxs = []int 0, // 0: xray.transport.internet.finalmask.header.custom.TCPSequence.sequence:type_name -> xray.transport.internet.finalmask.header.custom.TCPItem 1, // 1: xray.transport.internet.finalmask.header.custom.TCPConfig.clients:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence 1, // 2: xray.transport.internet.finalmask.header.custom.TCPConfig.servers:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence - 3, // 3: xray.transport.internet.finalmask.header.custom.UDPConfig.client:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem - 3, // 4: xray.transport.internet.finalmask.header.custom.UDPConfig.server:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 1, // 3: xray.transport.internet.finalmask.header.custom.TCPConfig.errors:type_name -> xray.transport.internet.finalmask.header.custom.TCPSequence + 3, // 4: xray.transport.internet.finalmask.header.custom.UDPConfig.client:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem + 3, // 5: xray.transport.internet.finalmask.header.custom.UDPConfig.server:type_name -> xray.transport.internet.finalmask.header.custom.UDPItem + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_transport_internet_finalmask_header_custom_config_proto_init() } diff --git a/transport/internet/finalmask/header/custom/config.proto b/transport/internet/finalmask/header/custom/config.proto index 2624157d46c4..34314dee9e5c 100644 --- a/transport/internet/finalmask/header/custom/config.proto +++ b/transport/internet/finalmask/header/custom/config.proto @@ -20,7 +20,7 @@ message TCPSequence { message TCPConfig { repeated TCPSequence clients = 1; repeated TCPSequence servers = 2; - bytes on_error = 3; + repeated TCPSequence errors = 3; } message UDPItem { diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index f5516f2ac097..8dada37aed68 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -173,7 +173,7 @@ func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { type tcpCustomServer struct { clients []*TCPSequence servers []*TCPSequence - onError []byte + errors []*TCPSequence merged [][]byte } @@ -192,7 +192,7 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { header: &tcpCustomServer{ clients: c.Clients, servers: c.Servers, - onError: c.OnError, + errors: c.Errors, }, } @@ -224,6 +224,31 @@ func (c *tcpCustomServerConn) Splice() bool { return true } +func (c *tcpCustomServerConn) sendError(index int) { + var merged []byte + if len(c.header.errors) > index { + for _, item := range c.header.errors[index].Sequence { + if item.DelayMax > 0 { + if len(merged) > 0 { + _, _ = c.Conn.Write(merged) + merged = nil + } + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + if item.Rand > 0 { + merged = append(merged, make([]byte, item.Rand)...) + common.Must2(rand.Read(merged[len(merged)-int(item.Rand):])) + } else { + merged = append(merged, item.Packet...) + } + } + if len(merged) > 0 { + _, _ = c.Conn.Write(merged) + merged = nil + } + } +} + func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { c.once.Do(func() { var buf [8192]byte @@ -240,16 +265,12 @@ func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { } if item.Rand > 0 { if n != length { - if len(c.header.onError) > 0 { - c.Conn.Write(c.header.onError) - } + c.sendError(i) c.wg.Done() return } } else if !bytes.Equal(item.Packet, buf[:n]) { - if len(c.header.onError) > 0 { - c.Conn.Write(c.header.onError) - } + c.sendError(i) c.wg.Done() return } From 218edcd3a8ae6fed7f7279bafa3430458598bdda Mon Sep 17 00:00:00 2001 From: null Date: Wed, 18 Feb 2026 20:04:26 +0800 Subject: [PATCH 47/75] fix infra --- infra/conf/transport_internet.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 65533ddfacc3..5720fd260855 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1282,6 +1282,9 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { func PraseByteSlice(data json.RawMessage, typ string) ([]byte, error) { switch strings.ToLower(typ) { case "", "array": + if len(data) == 0 { + return []byte{}, nil + } var packet []byte if err := json.Unmarshal(data, &packet); err != nil { return nil, err From 970f7aa1253135cd709fd5cb7c897363ecdbcb1b Mon Sep 17 00:00:00 2001 From: null Date: Thu, 19 Feb 2026 16:56:14 +0800 Subject: [PATCH 48/75] 1 --- infra/conf/transport_internet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 5720fd260855..09ba122b0d09 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1283,7 +1283,7 @@ func PraseByteSlice(data json.RawMessage, typ string) ([]byte, error) { switch strings.ToLower(typ) { case "", "array": if len(data) == 0 { - return []byte{}, nil + return data, nil } var packet []byte if err := json.Unmarshal(data, &packet); err != nil { From a1bd13e4e6ff57ef37b282a9bbc3fb3095a7afec Mon Sep 17 00:00:00 2001 From: null Date: Fri, 20 Feb 2026 01:06:23 +0800 Subject: [PATCH 49/75] tcp header: readSequence, writeSequence & remove 8192, header merged --- infra/conf/transport_internet.go | 9 - .../internet/finalmask/header/custom/tcp.go | 260 +++++------------- 2 files changed, 68 insertions(+), 201 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 09ba122b0d09..823de05b40ae 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -1352,9 +1352,6 @@ type HeaderCustomTCP struct { func (c *HeaderCustomTCP) Build() (proto.Message, error) { for _, value := range c.Clients { for _, item := range value { - if len(item.Packet) > 8192 || item.Rand > 8192 { - return nil, errors.New("len > 8192") - } if len(item.Packet) > 0 && item.Rand > 0 { return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") } @@ -1362,9 +1359,6 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { } for _, value := range c.Servers { for _, item := range value { - if len(item.Packet) > 8192 || item.Rand > 8192 { - return nil, errors.New("len > 8192") - } if len(item.Packet) > 0 && item.Rand > 0 { return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") } @@ -1372,9 +1366,6 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) { } for _, value := range c.Errors { for _, item := range value { - if len(item.Packet) > 8192 || item.Rand > 8192 { - return nil, errors.New("len > 8192") - } if len(item.Packet) > 0 && item.Rand > 0 { return nil, errors.New("len(item.Packet) > 0 && item.Rand > 0") } diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 8dada37aed68..024a8d48e50b 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -16,7 +16,6 @@ import ( type tcpCustomClient struct { clients []*TCPSequence servers []*TCPSequence - merged [][]byte } type tcpCustomClientConn struct { @@ -37,17 +36,6 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - conn.header.merged = make([][]byte, len(conn.header.clients)) - for index, client := range conn.header.clients { - for _, item := range client.Sequence { - if item.Rand > 0 { - conn.header.merged[index] = append(conn.header.merged[index], make([]byte, item.Rand)...) - } else { - conn.header.merged[index] = append(conn.header.merged[index], item.Packet...) - } - } - } - conn.wg.Add(1) return conn, nil @@ -77,82 +65,27 @@ func (c *tcpCustomClientConn) Read(p []byte) (n int, err error) { func (c *tcpCustomClientConn) Write(p []byte) (n int, err error) { c.once.Do(func() { - var buf [8192]byte - i := 0 j := 0 for i = range c.header.clients { - index := 0 - from := 0 - to := 0 - for to < len(c.header.merged[i]) { - item := c.header.clients[i].Sequence[index] - if item.DelayMax > 0 { - if to > from { - _, err := c.Conn.Write(c.header.merged[i][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to - } - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) - } - length := max(int(item.Rand), len(item.Packet)) - if item.Rand > 0 { - common.Must2(rand.Read(c.header.merged[i][to : to+length])) - } - to += length - index++ - } - if to > from { - _, err := c.Conn.Write(c.header.merged[i][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to + if !writeSequence(c.Conn, c.header.clients[i]) { + c.wg.Done() + return } if j < len(c.header.servers) { - for _, item := range c.header.servers[j].Sequence { - length := max(int(item.Rand), len(item.Packet)) - n, err := io.ReadFull(c.Conn, buf[:length]) - if err != nil { - c.wg.Done() - return - } - if item.Rand > 0 { - if n != length { - c.wg.Done() - return - } - } else if !bytes.Equal(item.Packet, buf[:n]) { - c.wg.Done() - return - } + if !readSequence(c.Conn, c.header.servers[j]) { + c.wg.Done() + return } j++ } } for j < len(c.header.servers) { - for _, item := range c.header.servers[j].Sequence { - length := max(int(item.Rand), len(item.Packet)) - n, err := io.ReadFull(c.Conn, buf[:length]) - if err != nil { - c.wg.Done() - return - } - if item.Rand > 0 { - if n != length { - c.wg.Done() - return - } - } else if !bytes.Equal(item.Packet, buf[:n]) { - c.wg.Done() - return - } + if !readSequence(c.Conn, c.header.servers[j]) { + c.wg.Done() + return } j++ } @@ -174,7 +107,6 @@ type tcpCustomServer struct { clients []*TCPSequence servers []*TCPSequence errors []*TCPSequence - merged [][]byte } type tcpCustomServerConn struct { @@ -196,17 +128,6 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { }, } - conn.header.merged = make([][]byte, len(conn.header.servers)) - for index, client := range conn.header.servers { - for _, item := range client.Sequence { - if item.Rand > 0 { - conn.header.merged[index] = append(conn.header.merged[index], make([]byte, item.Rand)...) - } else { - conn.header.merged[index] = append(conn.header.merged[index], item.Packet...) - } - } - } - conn.wg.Add(1) return conn, nil @@ -224,125 +145,32 @@ func (c *tcpCustomServerConn) Splice() bool { return true } -func (c *tcpCustomServerConn) sendError(index int) { - var merged []byte - if len(c.header.errors) > index { - for _, item := range c.header.errors[index].Sequence { - if item.DelayMax > 0 { - if len(merged) > 0 { - _, _ = c.Conn.Write(merged) - merged = nil - } - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) - } - if item.Rand > 0 { - merged = append(merged, make([]byte, item.Rand)...) - common.Must2(rand.Read(merged[len(merged)-int(item.Rand):])) - } else { - merged = append(merged, item.Packet...) - } - } - if len(merged) > 0 { - _, _ = c.Conn.Write(merged) - merged = nil - } - } -} - func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) { c.once.Do(func() { - var buf [8192]byte - i := 0 j := 0 for i = range c.header.clients { - for _, item := range c.header.clients[i].Sequence { - length := max(int(item.Rand), len(item.Packet)) - n, err := io.ReadFull(c.Conn, buf[:length]) - if err != nil { - c.wg.Done() - return - } - if item.Rand > 0 { - if n != length { - c.sendError(i) - c.wg.Done() - return - } - } else if !bytes.Equal(item.Packet, buf[:n]) { - c.sendError(i) - c.wg.Done() - return + if !readSequence(c.Conn, c.header.clients[i]) { + if i < len(c.header.errors) { + writeSequence(c.Conn, c.header.errors[i]) } + c.wg.Done() + return } if j < len(c.header.servers) { - index := 0 - from := 0 - to := 0 - for to < len(c.header.merged[j]) { - item := c.header.servers[j].Sequence[index] - if item.DelayMax > 0 { - if to > from { - _, err := c.Conn.Write(c.header.merged[j][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to - } - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) - } - length := max(int(item.Rand), len(item.Packet)) - if item.Rand > 0 { - common.Must2(rand.Read(c.header.merged[j][to : to+length])) - } - to += length - index++ - } - if to > from { - _, err := c.Conn.Write(c.header.merged[j][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to + if !writeSequence(c.Conn, c.header.servers[j]) { + c.wg.Done() + return } j++ } } for j < len(c.header.servers) { - index := 0 - from := 0 - to := 0 - for to < len(c.header.merged[j]) { - item := c.header.servers[j].Sequence[index] - if item.DelayMax > 0 { - if to > from { - _, err := c.Conn.Write(c.header.merged[j][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to - } - time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) - } - length := max(int(item.Rand), len(item.Packet)) - if item.Rand > 0 { - common.Must2(rand.Read(c.header.merged[j][to : to+length])) - } - to += length - index++ - } - if to > from { - _, err := c.Conn.Write(c.header.merged[j][from:to]) - if err != nil { - c.wg.Done() - return - } - from = to + if !writeSequence(c.Conn, c.header.servers[j]) { + c.wg.Done() + return } j++ } @@ -369,3 +197,51 @@ func (c *tcpCustomServerConn) Write(p []byte) (n int, err error) { return c.Conn.Write(p) } + +func readSequence(r io.Reader, sequence *TCPSequence) bool { + for _, item := range sequence.Sequence { + length := max(int(item.Rand), len(item.Packet)) + buf := make([]byte, length) + n, err := io.ReadFull(r, buf) + if err != nil { + return false + } + if item.Rand > 0 && n != length { + return false + } + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, buf[:n]) { + return false + } + } + return true +} + +func writeSequence(w io.Writer, sequence *TCPSequence) bool { + var merged []byte + for _, item := range sequence.Sequence { + if item.DelayMax > 0 { + if len(merged) > 0 { + _, err := w.Write(merged) + if err != nil { + return false + } + merged = nil + } + time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) + } + if item.Rand > 0 { + merged = append(merged, make([]byte, item.Rand)...) + common.Must2(rand.Read(merged[len(merged)-int(item.Rand):])) + } else { + merged = append(merged, item.Packet...) + } + } + if len(merged) > 0 { + _, err := w.Write(merged) + if err != nil { + return false + } + merged = nil + } + return true +} From bf2fab32320a671ab815be7cb9d075f8fc2e246b Mon Sep 17 00:00:00 2001 From: null Date: Fri, 20 Feb 2026 01:29:37 +0800 Subject: [PATCH 50/75] 1 --- transport/internet/finalmask/header/custom/tcp.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index 024a8d48e50b..b30c6a16a462 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -230,8 +230,9 @@ func writeSequence(w io.Writer, sequence *TCPSequence) bool { time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond) } if item.Rand > 0 { - merged = append(merged, make([]byte, item.Rand)...) - common.Must2(rand.Read(merged[len(merged)-int(item.Rand):])) + buf := make([]byte, item.Rand) + common.Must2(rand.Read(buf)) + merged = append(merged, buf...) } else { merged = append(merged, item.Packet...) } From 38f6a65ad508ced1c279ff87ad8cc0a6ddc0e04e Mon Sep 17 00:00:00 2001 From: null Date: Sat, 21 Feb 2026 17:59:18 +0800 Subject: [PATCH 51/75] refactor udpmask --- transport/internet/finalmask/finalmask.go | 18 +- .../finalmask/header/custom/config.go | 8 +- .../internet/finalmask/header/custom/udp.go | 323 ++++++------------ .../internet/finalmask/header/dns/config.go | 8 +- .../internet/finalmask/header/dns/conn.go | 126 ++----- .../internet/finalmask/header/dtls/config.go | 8 +- .../internet/finalmask/header/dtls/conn.go | 124 ++----- .../internet/finalmask/header/srtp/config.go | 8 +- .../internet/finalmask/header/srtp/conn.go | 124 ++----- .../internet/finalmask/header/utp/config.go | 8 +- .../internet/finalmask/header/utp/conn.go | 124 ++----- .../finalmask/header/wechat/config.go | 8 +- .../internet/finalmask/header/wechat/conn.go | 124 ++----- .../finalmask/header/wireguard/config.go | 8 +- .../finalmask/header/wireguard/conn.go | 124 ++----- .../finalmask/mkcp/aes128gcm/config.go | 8 +- .../internet/finalmask/mkcp/aes128gcm/conn.go | 161 ++++----- .../finalmask/mkcp/original/config.go | 8 +- .../internet/finalmask/mkcp/original/conn.go | 138 +++----- .../finalmask/mkcp/original/simple_test.go | 16 + transport/internet/finalmask/noise/config.go | 8 +- transport/internet/finalmask/noise/conn.go | 15 +- .../internet/finalmask/salamander/config.go | 8 +- .../internet/finalmask/salamander/conn.go | 131 +++---- transport/internet/finalmask/xdns/client.go | 10 +- transport/internet/finalmask/xdns/config.go | 8 +- transport/internet/finalmask/xdns/server.go | 10 +- transport/internet/finalmask/xicmp/client.go | 8 +- transport/internet/finalmask/xicmp/config.go | 8 +- transport/internet/finalmask/xicmp/server.go | 4 +- 30 files changed, 545 insertions(+), 1139 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 8838e9506cf3..68bee206e958 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -5,18 +5,14 @@ import ( ) const ( - UDPSize = 4096 + UDPSize = 4096 + 123 ) -type ConnSize interface { - Size() int32 -} - type Udpmask interface { UDP() - 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) + WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) + WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) } type UdpmaskManager struct { @@ -30,27 +26,23 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager { } func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) { - leaveSize := int32(0) var err error for i, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnClient(raw, i == len(m.udpmasks)-1, leaveSize, i == 0) + raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1) 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 i, mask := range m.udpmasks { - raw, err = mask.WrapPacketConnServer(raw, i == len(m.udpmasks)-1, leaveSize, i == 0) + raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1) if err != nil { return nil, err } - leaveSize += raw.(ConnSize).Size() } return raw, nil } diff --git a/transport/internet/finalmask/header/custom/config.go b/transport/internet/finalmask/header/custom/config.go index 9e0441027a1f..1d72e336c0cd 100644 --- a/transport/internet/finalmask/header/custom/config.go +++ b/transport/internet/finalmask/header/custom/config.go @@ -18,10 +18,10 @@ func (c *TCPConfig) WrapConnServer(raw net.Conn) (net.Conn, error) { func (c *UDPConfig) UDP() { } -func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnClientUDP(c, raw, first, leaveSize) +func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClientUDP(c, raw) } -func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServerUDP(c, raw, first, leaveSize) +func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServerUDP(c, raw) } diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 037daf49e1d8..24c1772cf42c 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -4,9 +4,8 @@ import ( "bytes" "context" "crypto/rand" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" @@ -32,24 +31,35 @@ func (h *udpCustomClient) Serialize(b []byte) { copy(b, h.merged) } -type udpCustomClientConn struct { - first bool - leaveSize int32 +func (h *udpCustomClient) Match(b []byte) bool { + if len(b) < len(h.merged) { + return false + } + + data := b + match := true + + for _, item := range h.server { + length := max(int(item.Rand), len(item.Packet)) + + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) { + match = false + break + } + + data = data[length:] + } + return match +} + +type udpCustomClientConn struct { net.PacketConn header *udpCustomClient - - readBuf []byte - readMutex sync.Mutex - writeBuf []byte - writeMutex sync.Mutex } -func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { +func NewConnClientUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) { conn := &udpCustomClientConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &udpCustomClient{ client: c.Client, @@ -57,11 +67,6 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - index := 0 for _, item := range conn.header.client { if item.Rand > 0 { @@ -76,112 +81,54 @@ func NewConnClientUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in return conn, nil } -func (c *udpCustomClientConn) Size() int32 { - return int32(len(c.header.merged)) -} - func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - index := 0 - mismatch := false - for _, item := range c.header.server { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { - mismatch = true - break - } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { - mismatch = true - break - } - index += length - } - - if mismatch { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - continue - } - - copy(p, c.readBuf[index:n]) - - if len(p) < n-index { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-index) - continue - } - - c.readMutex.Unlock() - return n - index, addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - index := 0 - for _, item := range c.header.server { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { - return 0, addr, errors.New("header mismatch") - } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, p[index:index+length]) { - return 0, addr, errors.New("header mismatch") - } - index += length + if !c.header.Match(buf[:n]) { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-len(c.header.merged) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) + return 0, addr, io.ErrShortBuffer } - copy(p, p[index:n]) + copy(p, buf[len(c.header.merged):n]) - return n - index, addr, nil + return n - len(c.header.merged), addr, nil } func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if len(c.header.merged)+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:len(c.header.merged)+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[len(c.header.merged):], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } type udpCustomServer struct { @@ -203,24 +150,35 @@ func (h *udpCustomServer) Serialize(b []byte) { copy(b, h.merged) } -type udpCustomServerConn struct { - first bool - leaveSize int32 +func (h *udpCustomServer) Match(b []byte) bool { + if len(b) < len(h.merged) { + return false + } + + data := b + match := true + + for _, item := range h.client { + length := max(int(item.Rand), len(item.Packet)) + + if len(item.Packet) > 0 && !bytes.Equal(item.Packet, data[:length]) { + match = false + break + } + + data = data[length:] + } + return match +} + +type udpCustomServerConn struct { net.PacketConn header *udpCustomServer - - readBuf []byte - readMutex sync.Mutex - writeBuf []byte - writeMutex sync.Mutex } -func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { +func NewConnServerUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) { conn := &udpCustomServerConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &udpCustomServer{ client: c.Client, @@ -228,11 +186,6 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - index := 0 for _, item := range conn.header.server { if item.Rand > 0 { @@ -247,110 +200,52 @@ func NewConnServerUDP(c *UDPConfig, raw net.PacketConn, first bool, leaveSize in return conn, nil } -func (c *udpCustomServerConn) Size() int32 { - return int32(len(c.header.merged)) -} - func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - index := 0 - mismatch := false - for _, item := range c.header.client { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { - mismatch = true - break - } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, c.readBuf[index:index+length]) { - mismatch = true - break - } - index += length - } - - if mismatch { - errors.LogDebug(context.Background(), addr, " mask read err header mismatch") - continue - } - - copy(p, c.readBuf[index:n]) - - if len(p) < n-index { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-index) - continue - } - - c.readMutex.Unlock() - return n - index, addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - index := 0 - for _, item := range c.header.client { - length := max(int(item.Rand), len(item.Packet)) - if index+length > n { - return 0, addr, errors.New("header mismatch") - } - if len(item.Packet) > 0 && !bytes.Equal(item.Packet, p[index:index+length]) { - return 0, addr, errors.New("header mismatch") - } - index += length + if !c.header.Match(buf[:n]) { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-len(c.header.merged) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) + return 0, addr, io.ErrShortBuffer } - copy(p, p[index:n]) + copy(p, buf[len(c.header.merged):n]) - return n - index, addr, nil + return n - len(c.header.merged), addr, nil } func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if len(c.header.merged)+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:len(c.header.merged)+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[len(c.header.merged):], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/dns/config.go b/transport/internet/finalmask/header/dns/config.go index d5aa5cc399e0..7be9eb9aae74 100644 --- a/transport/internet/finalmask/header/dns/config.go +++ b/transport/internet/finalmask/header/dns/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index 684ae68f85f6..b3e85d13485f 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -3,9 +3,8 @@ package dns import ( "context" "encoding/binary" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -82,8 +81,8 @@ type dns struct { header []byte } -func (h *dns) Size() int32 { - return int32(len(h.header)) +func (h *dns) Size() int { + return len(h.header) } func (h *dns) Serialize(b []byte) { @@ -92,19 +91,11 @@ func (h *dns) Serialize(b []byte) { } type dnsConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { var header []byte header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query @@ -122,108 +113,65 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN conn := &dnsConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &dns{ header: header, }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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()]) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } - - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/dtls/config.go b/transport/internet/finalmask/header/dtls/config.go index ccce33decfdb..02d102d06e59 100644 --- a/transport/internet/finalmask/header/dtls/config.go +++ b/transport/internet/finalmask/header/dtls/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index f3508dc9a2c7..9a78afd86450 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -2,9 +2,8 @@ package dtls import ( "context" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -17,7 +16,7 @@ type dtls struct { sequence uint32 } -func (*dtls) Size() int32 { +func (*dtls) Size() int { return 1 + 2 + 2 + 6 + 2 } @@ -43,23 +42,12 @@ func (h *dtls) Serialize(b []byte) { } type dtlsConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &dtlsConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &dtls{ epoch: dice.RollUint16(), @@ -68,99 +56,59 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/srtp/config.go b/transport/internet/finalmask/header/srtp/config.go index 45def61629ca..875bf899f27d 100644 --- a/transport/internet/finalmask/header/srtp/config.go +++ b/transport/internet/finalmask/header/srtp/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index dd2e78cf1810..29c1ed5a2d16 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -3,9 +3,8 @@ package srtp import ( "context" "encoding/binary" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -17,7 +16,7 @@ type srtp struct { number uint16 } -func (*srtp) Size() int32 { +func (*srtp) Size() int { return 4 } @@ -28,23 +27,12 @@ func (h *srtp) Serialize(b []byte) { } type srtpConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &srtpConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &srtp{ header: 0xB5E8, @@ -52,99 +40,59 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/utp/config.go b/transport/internet/finalmask/header/utp/config.go index a579d48366e8..804f462acefc 100644 --- a/transport/internet/finalmask/header/utp/config.go +++ b/transport/internet/finalmask/header/utp/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 63ff768e258a..a184bf5447f3 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -3,9 +3,8 @@ package utp import ( "context" "encoding/binary" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -18,7 +17,7 @@ type utp struct { connectionID uint16 } -func (*utp) Size() int32 { +func (*utp) Size() int { return 4 } @@ -29,23 +28,12 @@ func (h *utp) Serialize(b []byte) { } type utpConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &utpConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &utp{ header: 1, @@ -54,99 +42,59 @@ func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) ( }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/wechat/config.go b/transport/internet/finalmask/header/wechat/config.go index 34971ace3a72..8cd6ad3f9b73 100644 --- a/transport/internet/finalmask/header/wechat/config.go +++ b/transport/internet/finalmask/header/wechat/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index b9a82dc1c57a..b7fb4b6bb6dd 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -3,9 +3,8 @@ package wechat import ( "context" "encoding/binary" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" @@ -16,7 +15,7 @@ type wechat struct { sn uint32 } -func (*wechat) Size() int32 { +func (*wechat) Size() int { return 13 } @@ -35,122 +34,71 @@ func (h *wechat) Serialize(b []byte) { } type wechatConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &wechatConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &wechat{ sn: uint32(dice.RollUint16()), }, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/header/wireguard/config.go b/transport/internet/finalmask/header/wireguard/config.go index 5eeee34ba6ae..b159f438a994 100644 --- a/transport/internet/finalmask/header/wireguard/config.go +++ b/transport/internet/finalmask/header/wireguard/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index f83d689cddde..45841113dd87 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -2,9 +2,8 @@ package wireguard import ( "context" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet/finalmask" @@ -12,7 +11,7 @@ import ( type wireguare struct{} -func (*wireguare) Size() int32 { +func (*wireguare) Size() int { return 4 } @@ -24,120 +23,69 @@ func (h *wireguare) Serialize(b []byte) { } type wireguareConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &wireguareConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, header: &wireguare{}, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - copy(p, c.readBuf[c.Size():n]) - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err header mismatch") + return 0, addr, nil + } + + if len(p) < n-c.header.Size() { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) + return 0, addr, io.ErrShortBuffer } - copy(p, p[c.Size():n]) + copy(p, buf[c.header.Size():n]) - return n - int(c.Size()), addr, nil + return n - c.header.Size(), addr, nil } func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if c.first { - if c.leaveSize+c.Size()+int32(len(p)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.header.Size()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.header.Size()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + copy(buf[c.header.Size():], p) + c.header.Serialize(buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.header.Size()+len(p)], addr) + if err != nil { + return 0, err } - c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/config.go b/transport/internet/finalmask/mkcp/aes128gcm/config.go index 595dd4ee93cb..da3459c6d1e5 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/config.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 1fd7bf76cb5e..6f37bb2eb5c2 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -5,9 +5,8 @@ import ( "crypto/cipher" "crypto/rand" "crypto/sha256" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" @@ -16,92 +15,64 @@ import ( ) type aes128gcmConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { hashedPsk := sha256.Sum256([]byte(c.Password)) conn := &aes128gcmConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, aead: crypto.NewAesGcm(hashedPsk[:16]), } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - 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 { - errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) - continue - } - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil + if len(p) < finalmask.UDPSize { + buf := make([]byte, finalmask.UDPSize) + + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { + return n, addr, err } + + if n < c.aead.NonceSize()+c.aead.Overhead() { + errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) + return 0, addr, nil + } + + nonceSize := c.aead.NonceSize() + nonce := buf[:nonceSize] + ciphertext := buf[nonceSize:n] + plaintext, err := c.aead.Open(p[:0], nonce, ciphertext, nil) + if err != nil { + errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) + return 0, addr, nil + } + + if len(plaintext) > len(p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(plaintext)) + return 0, addr, io.ErrShortBuffer + } + + return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil } n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.aead.NonceSize()+c.aead.Overhead() { + errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) + return 0, addr, nil } nonceSize := c.aead.NonceSize() @@ -109,54 +80,40 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 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) + errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) + return 0, addr, nil } + copy(p, p[nonceSize:n-c.aead.Overhead()]) - return n - int(c.Size()), addr, nil + return n - c.aead.NonceSize() - c.aead.Overhead(), 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)) > finalmask.UDPSize { - 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.PacketConn.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") - } + if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - c.writeMutex.Unlock() - return len(p), nil + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.aead.NonceSize()+c.aead.Overhead()+len(p)] + copy(buf[c.aead.NonceSize():], p) + p = buf[c.aead.NonceSize() : c.aead.NonceSize()+len(p)] } nonceSize := c.aead.NonceSize() - nonce := p[c.leaveSize : c.leaveSize+int32(nonceSize)] + nonce := buf[: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) + ciphertext := buf[nonceSize : c.aead.NonceSize()+c.aead.Overhead()+len(p)] + _ = c.aead.Seal(ciphertext[:0], nonce, p, nil) + + _, err = c.PacketConn.WriteTo(buf[:c.aead.NonceSize()+c.aead.Overhead()+len(p)], addr) + if err != nil { + return 0, err + } - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/mkcp/original/config.go b/transport/internet/finalmask/mkcp/original/config.go index 026c979d625a..19db41383508 100644 --- a/transport/internet/finalmask/mkcp/original/config.go +++ b/transport/internet/finalmask/mkcp/original/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index 563ef5105ab0..da55a2bb0557 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -4,10 +4,9 @@ import ( "context" "crypto/cipher" "encoding/binary" - go_errors "errors" "hash/fnv" + "io" "net" - "sync" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" @@ -76,138 +75,77 @@ func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) { } type simpleConn struct { - first bool - leaveSize int32 - 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) { +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &simpleConn{ - first: first, - leaveSize: leaveSize, - PacketConn: raw, aead: &simple{}, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - 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 NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - if c.first { - c.readMutex.Lock() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - ciphertext := c.readBuf[:n] - opened, err := c.aead.Open(nil, nil, ciphertext, nil) - if err != nil { - errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) - continue - } - - copy(p, opened) - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < c.aead.Overhead() { + errors.LogDebug(context.Background(), addr, " mask read err aead short lenth ", n) + return 0, addr, nil } - ciphertext := p[:n] + ciphertext := buf[: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) + errors.LogDebug(context.Background(), addr, " mask read err aead open ", err) + return 0, addr, nil + } + + if len(opened) > len(p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(opened)) + return 0, addr, io.ErrShortBuffer } copy(p, opened) - return n - int(c.Size()), addr, nil + return n - c.aead.Overhead(), 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)) > finalmask.UDPSize { - 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.PacketConn.WriteTo(c.writeBuf[:n], addr) + if c.aead.Overhead()+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:c.aead.Overhead()+len(p)] + copy(buf[c.aead.Overhead():], p) + p = buf[c.aead.Overhead() : c.aead.Overhead()+len(p)] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + _ = c.aead.Seal(buf[:0], nil, p, nil) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:c.aead.Overhead()+len(p)], addr) + if err != nil { + return 0, err } - plaintext := p[c.leaveSize+c.Size():] - sealed := c.aead.Seal(nil, nil, plaintext, nil) - copy(p[c.leaveSize:], sealed) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/mkcp/original/simple_test.go b/transport/internet/finalmask/mkcp/original/simple_test.go index f7db54748df8..be9d68398f37 100644 --- a/transport/internet/finalmask/mkcp/original/simple_test.go +++ b/transport/internet/finalmask/mkcp/original/simple_test.go @@ -8,6 +8,22 @@ import ( "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original" ) +func TestSimpleSealInPlace(t *testing.T) { + aead := original.NewSimple() + + text := []byte("0123456789012") + buf := make([]byte, 8192) + + copy(buf[aead.Overhead():], text) + plaintext := buf[aead.Overhead() : aead.Overhead()+len(text)] + + sealed := aead.Seal(nil, nil, plaintext, nil) + + _ = aead.Seal(buf[:0], nil, plaintext, nil) + + assert.Equal(t, sealed, buf[:aead.Overhead()+len(text)]) +} + func TestOriginalBounce(t *testing.T) { aead := original.NewSimple() buf := make([]byte, aead.NonceSize()+aead.Overhead()) diff --git a/transport/internet/finalmask/noise/config.go b/transport/internet/finalmask/noise/config.go index b3dabd058808..1764c0b19b19 100644 --- a/transport/internet/finalmask/noise/config.go +++ b/transport/internet/finalmask/noise/config.go @@ -5,10 +5,10 @@ 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, end) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/noise/conn.go b/transport/internet/finalmask/noise/conn.go index b37a2bf77326..022fb21bcb62 100644 --- a/transport/internet/finalmask/noise/conn.go +++ b/transport/internet/finalmask/noise/conn.go @@ -8,7 +8,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" - "github.com/xtls/xray-core/common/errors" ) type noiseConn struct { @@ -20,11 +19,7 @@ type noiseConn struct { mutex sync.RWMutex } -func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { - if !end { - return nil, errors.New("noise requires being at the outermost level") - } - +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { conn := &noiseConn{ PacketConn: raw, config: c, @@ -39,8 +34,8 @@ func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, err return conn, nil } -func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { - return NewConnClient(c, raw, end) +func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } func (c *noiseConn) reset() { @@ -70,10 +65,6 @@ func (c *noiseConn) reset() { } } -func (c *noiseConn) Size() int32 { - return 0 -} - func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.mutex.RLock() _, ready := c.m[addr.String()] diff --git a/transport/internet/finalmask/salamander/config.go b/transport/internet/finalmask/salamander/config.go index c864e270d666..371b528c61c1 100644 --- a/transport/internet/finalmask/salamander/config.go +++ b/transport/internet/finalmask/salamander/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, first, leaveSize) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 91feb1e687ab..03c9fcb4d9d5 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -2,134 +2,83 @@ package salamander import ( "context" - go_errors "errors" + "io" "net" - "sync" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet/finalmask" ) -type obfsPacketConn struct { - first bool - leaveSize int32 - +type salamanderConn struct { 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) { +func NewConnClient(c *Config, raw net.PacketConn) (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 := &salamanderConn{ PacketConn: raw, obfs: ob, } - if first { - conn.readBuf = make([]byte, finalmask.UDPSize) - conn.writeBuf = make([]byte, finalmask.UDPSize) - } - return conn, nil } -func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) { - return NewConnClient(c, raw, first, leaveSize) +func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { + return NewConnClient(c, raw) } -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() - - for { - n, addr, err = c.PacketConn.ReadFrom(c.readBuf) - if err != nil { - var ne net.Error - if go_errors.As(err, &ne) { - c.readMutex.Unlock() - return n, addr, err - } - errors.LogDebug(context.Background(), addr, " mask read err ", err) - continue - } - - if n < int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short lenth") - continue - } - - if len(p) < n-int(c.Size()) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-int(c.Size())) - continue - } - - c.obfs.Deobfuscate(c.readBuf[:n], p) - - c.readMutex.Unlock() - return n - int(c.Size()), addr, nil - } +func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + buf := p + if len(p) < finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) } - n, addr, err = c.PacketConn.ReadFrom(p) - if err != nil { + n, addr, err = c.PacketConn.ReadFrom(buf) + if err != nil || n == 0 { return n, addr, err } - if n < int(c.Size()) { - return 0, addr, errors.New("short lenth") + if n < smSaltLen { + errors.LogDebug(context.Background(), addr, " mask read err short lenth ", n) + return 0, addr, nil } - 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)) > finalmask.UDPSize { - return 0, errors.New("too many masks") - } - - c.writeMutex.Lock() + if len(p) < n-smSaltLen { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-smSaltLen) + return 0, addr, io.ErrShortBuffer + } - n = copy(c.writeBuf[c.leaveSize+c.Size():], p) - n += int(c.leaveSize) + int(c.Size()) + c.obfs.Deobfuscate(buf[:n], p) - c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n]) + return n - smSaltLen, addr, nil +} - nn, err := c.PacketConn.WriteTo(c.writeBuf[:n], addr) +func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if smSaltLen+len(p) > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize) + return 0, io.ErrShortWrite + } - if err != nil { - c.writeMutex.Unlock() - return 0, err - } + var buf []byte + if cap(p) != finalmask.UDPSize { + buf = make([]byte, finalmask.UDPSize) + } else { + buf = p[:smSaltLen+len(p)] + copy(buf[smSaltLen:], p) + p = buf[smSaltLen:] + } - if nn != n { - c.writeMutex.Unlock() - return 0, errors.New("nn != n") - } + c.obfs.Obfuscate(p, buf) - c.writeMutex.Unlock() - return len(p), nil + _, err = c.PacketConn.WriteTo(buf[:smSaltLen+len(p)], addr) + if err != nil { + return 0, err } - c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:]) - - return c.PacketConn.WriteTo(p, addr) + return len(p), nil } diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 199c67beb8ac..7ad9d03dd075 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -46,11 +46,7 @@ type xdnsConnClient struct { 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") - } - +func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { domain, err := ParseName(c.Domain) if err != nil { return nil, err @@ -190,10 +186,6 @@ func (c *xdnsConnClient) sendLoop() { } } -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 { diff --git a/transport/internet/finalmask/xdns/config.go b/transport/internet/finalmask/xdns/config.go index cf30902aee4d..157102dafa2b 100644 --- a/transport/internet/finalmask/xdns/config.go +++ b/transport/internet/finalmask/xdns/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, end) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw) } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index b50e42193ffb..43eed712d59a 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -62,11 +62,7 @@ type xdnsConnServer struct { 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") - } - +func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { domain, err := ParseName(c.Domain) if err != nil { return nil, err @@ -320,10 +316,6 @@ func (c *xdnsConnServer) sendLoop() { } } -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 { diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 221c93ac8861..0917bb7437f4 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -54,10 +54,10 @@ type xicmpConnClient struct { mutex sync.Mutex } -func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { +func NewConnClient(c *Config, raw net.PacketConn, level int) (net.PacketConn, error) { _, ok1 := raw.(*internet.FakePacketConn) _, ok2 := raw.(*udphop.UdpHopPacketConn) - if !end || ok1 || ok2 { + if level != 0 || ok1 || ok2 { return nil, errors.New("xicmp requires being at the outermost level") } @@ -280,10 +280,6 @@ func (c *xicmpConnClient) sendLoop() { } } -func (c *xicmpConnClient) Size() int32 { - return 0 -} - func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { diff --git a/transport/internet/finalmask/xicmp/config.go b/transport/internet/finalmask/xicmp/config.go index 81a483af8539..c570ce96817e 100644 --- a/transport/internet/finalmask/xicmp/config.go +++ b/transport/internet/finalmask/xicmp/config.go @@ -7,10 +7,10 @@ import ( 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) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnClient(c, raw, level) } -func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) { - return NewConnServer(c, raw, end) +func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) { + return NewConnServer(c, raw, level) } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 9fa0c461f22e..89ba5f488779 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -49,8 +49,8 @@ type xicmpConnServer struct { mutex sync.Mutex } -func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) { - if !end { +func NewConnServer(c *Config, raw net.PacketConn, level int) (net.PacketConn, error) { + if level != 0 { return nil, errors.New("xicmp requires being at the outermost level") } From 4e0a634f24234420ea7f7a9503856c85d27e5fa2 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 21 Feb 2026 18:01:36 +0800 Subject: [PATCH 52/75] 1 --- transport/internet/finalmask/xicmp/server.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 89ba5f488779..1a01b9afe6a5 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -302,10 +302,6 @@ func (c *xicmpConnServer) sendLoop() { } } -func (c *xicmpConnServer) Size() int32 { - return 0 -} - func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { From fe02453523a97bdab8863c4a463fade2e0904d60 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 21 Feb 2026 20:23:14 +0800 Subject: [PATCH 53/75] refactor udpmask --- .../internet/finalmask/header/custom/udp.go | 9 ++-- .../internet/finalmask/header/dns/conn.go | 5 +- .../internet/finalmask/header/dtls/conn.go | 5 +- .../internet/finalmask/header/srtp/conn.go | 5 +- .../internet/finalmask/header/utp/conn.go | 5 +- .../internet/finalmask/header/wechat/conn.go | 5 +- .../finalmask/header/wireguard/conn.go | 5 +- .../internet/finalmask/mkcp/aes128gcm/conn.go | 5 +- .../internet/finalmask/mkcp/original/conn.go | 5 +- .../internet/finalmask/salamander/conn.go | 5 +- transport/internet/finalmask/xdns/client.go | 32 +++++++----- transport/internet/finalmask/xdns/server.go | 30 ++++++++---- transport/internet/finalmask/xicmp/client.go | 39 +++++++-------- transport/internet/finalmask/xicmp/server.go | 49 +++++++++---------- 14 files changed, 106 insertions(+), 98 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index 24c1772cf42c..efecf90b123d 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "crypto/rand" - "io" "net" "github.com/xtls/xray-core/common" @@ -99,7 +98,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro if len(p) < n-len(c.header.merged) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[len(c.header.merged):n]) @@ -110,7 +109,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte @@ -218,7 +217,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro if len(p) < n-len(c.header.merged) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-len(c.header.merged)) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[len(c.header.merged):n]) @@ -229,7 +228,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index b3e85d13485f..a60aa2e4f571 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -3,7 +3,6 @@ package dns import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -144,7 +143,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -155,7 +154,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 9a78afd86450..0f7c16e8e386 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -2,7 +2,6 @@ package dtls import ( "context" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -81,7 +80,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -92,7 +91,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 29c1ed5a2d16..8dabcd5f0485 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -3,7 +3,6 @@ package srtp import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -65,7 +64,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -76,7 +75,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index a184bf5447f3..7647e0490f3f 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -3,7 +3,6 @@ package utp import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -67,7 +66,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -78,7 +77,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index b7fb4b6bb6dd..157c1947112f 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -3,7 +3,6 @@ package wechat import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -71,7 +70,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -82,7 +81,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 45841113dd87..2d310d282f36 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -2,7 +2,6 @@ package wireguard import ( "context" - "io" "net" "github.com/xtls/xray-core/common/errors" @@ -58,7 +57,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-c.header.Size() { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-c.header.Size()) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, buf[c.header.Size():n]) @@ -69,7 +68,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 6f37bb2eb5c2..89fd90317b05 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" "crypto/rand" "crypto/sha256" - "io" "net" "github.com/xtls/xray-core/common" @@ -59,7 +58,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(plaintext) > len(p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(plaintext)) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } return n - c.aead.NonceSize() - c.aead.Overhead(), addr, nil @@ -92,7 +91,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index da55a2bb0557..898541bdc649 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" "encoding/binary" "hash/fnv" - "io" "net" "github.com/xtls/xray-core/common" @@ -117,7 +116,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(opened) > len(p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(opened)) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } copy(p, opened) @@ -128,7 +127,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 03c9fcb4d9d5..f693ef07ad62 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -2,7 +2,6 @@ package salamander import ( "context" - "io" "net" "github.com/xtls/xray-core/common/errors" @@ -50,7 +49,7 @@ func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if len(p) < n-smSaltLen { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", n-smSaltLen) - return 0, addr, io.ErrShortBuffer + return 0, addr, nil } c.obfs.Deobfuscate(buf[:n], p) @@ -61,7 +60,7 @@ func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if smSaltLen+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 7ad9d03dd075..7decc149dd3b 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -75,9 +75,13 @@ func (c *xdnsConnClient) recvLoop() { var buf [finalmask.UDPSize]byte for { + if c.closed { + break + } + n, addr, err := c.PacketConn.ReadFrom(buf[:]) if err != nil { - if go_errors.Is(err, net.ErrClosed) { + if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) { break } continue @@ -189,14 +193,14 @@ func (c *xdnsConnClient) sendLoop() { func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, net.ErrClosed + return 0, nil, io.EOF } - n = copy(p, packet.p) - if n != len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) - return n, packet.addr, io.ErrShortBuffer + if len(p) < len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + return 0, packet.addr, nil } - return n, packet.addr, nil + copy(p, packet.p) + return len(packet.p), packet.addr, nil } func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { @@ -204,13 +208,13 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { defer c.mutex.Unlock() if c.closed { - return 0, errors.New("xdns closed") + return 0, io.ErrClosedPipe } 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) + errors.LogDebug(context.Background(), addr, " mask write err ", err) + return 0, nil } select { @@ -220,10 +224,16 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { }: return len(p), nil default: - return 0, errors.New("xdns queue full") + errors.LogDebug(context.Background(), addr, " mask write err queue full") + return 0, nil } } +func (c *xdnsConnClient) Close() error { + c.closed = true + return c.PacketConn.Close() +} + func encode(p []byte, clientID []byte, domain Name) ([]byte, error) { var decoded []byte { diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 43eed712d59a..0da67365baf2 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -154,9 +154,13 @@ func (c *xdnsConnServer) recvLoop() { var buf [finalmask.UDPSize]byte for { + if c.closed { + break + } + n, addr, err := c.PacketConn.ReadFrom(buf[:]) if err != nil { - if go_errors.Is(err, net.ErrClosed) { + if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) { break } continue @@ -319,27 +323,27 @@ func (c *xdnsConnServer) sendLoop() { func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, net.ErrClosed + return 0, nil, io.EOF } - n = copy(p, packet.p) - if n != len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) - return n, packet.addr, io.ErrShortBuffer + if len(p) < len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + return 0, packet.addr, nil } - return n, packet.addr, nil + copy(p, packet.p) + return len(packet.p), 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") + return 0, io.ErrClosedPipe } c.mutex.Lock() defer c.mutex.Unlock() if c.closed { - return 0, errors.New("xdns closed") + return 0, io.ErrClosedPipe } buf := make([]byte, len(p)) @@ -349,10 +353,16 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { case q.queue <- buf: return len(p), nil default: - return 0, errors.New("xdns queue full") + errors.LogDebug(context.Background(), addr, " mask write err queue full") + return 0, nil } } +func (c *xdnsConnServer) Close() error { + c.closed = true + return c.PacketConn.Close() +} + func nextPacketServer(r *bytes.Reader) ([]byte, error) { eof := func(err error) error { if err == io.EOF { diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 0917bb7437f4..9d5dbbf84ca1 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -149,13 +149,13 @@ func (c *xicmpConnClient) encode(p []byte) ([]byte, error) { } func (c *xicmpConnClient) recvLoop() { + var buf [finalmask.UDPSize]byte + for { if c.closed { break } - var buf [finalmask.UDPSize]byte - n, addr, err := c.icmpConn.ReadFrom(buf[:]) if err != nil { continue @@ -217,6 +217,12 @@ func (c *xicmpConnClient) recvLoop() { close(c.pollChan) close(c.readQueue) + + c.mutex.Lock() + defer c.mutex.Unlock() + + c.closed = true + close(c.writeQueue) } func (c *xicmpConnClient) sendLoop() { @@ -283,27 +289,28 @@ func (c *xicmpConnClient) sendLoop() { func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, net.ErrClosed + return 0, nil, io.EOF } - n = copy(p, packet.p) - if n != len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) - return n, packet.addr, io.ErrShortBuffer + if len(p) < len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + return 0, packet.addr, nil } - return n, packet.addr, nil + copy(p, packet.p) + return len(packet.p), packet.addr, nil } func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := c.encode(p) if err != nil { - return 0, errors.New("xicmp encode").Base(err) + errors.LogDebug(context.Background(), addr, " mask write err ", err) + return 0, nil } c.mutex.Lock() defer c.mutex.Unlock() if c.closed { - return 0, errors.New("xicmp closed") + return 0, io.ErrClosedPipe } select { @@ -313,21 +320,13 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { }: return len(p), nil default: - return 0, errors.New("xicmp queue full") + errors.LogDebug(context.Background(), addr, " mask write err queue full") + return 0, nil } } func (c *xicmpConnClient) Close() error { - c.mutex.Lock() - defer c.mutex.Unlock() - - if c.closed { - return nil - } - c.closed = true - close(c.writeQueue) - _ = c.icmpConn.Close() return c.conn.Close() } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 1a01b9afe6a5..e249d793f37a 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -10,6 +10,7 @@ import ( "github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/transport/internet/finalmask" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" @@ -159,8 +160,8 @@ func (c *xicmpConnServer) encode(p []byte, id int, seq int, needSeqByte bool, se return nil, err } - if len(buf) > 8192 { - return nil, errors.New("xicmp len(buf) > 8192") + if len(buf) > finalmask.UDPSize { + return nil, errors.New("xicmp len(buf) > finalmask.UDPSize") } return buf, nil @@ -177,13 +178,13 @@ func (c *xicmpConnServer) randUntil(b1 byte) byte { } func (c *xicmpConnServer) recvLoop() { + var buf [finalmask.UDPSize]byte + for { if c.closed { break } - var buf [8192]byte - n, addr, err := c.icmpConn.ReadFrom(buf[:]) if err != nil { continue @@ -245,6 +246,15 @@ func (c *xicmpConnServer) recvLoop() { close(c.ch) close(c.readQueue) + + c.mutex.Lock() + defer c.mutex.Unlock() + + c.closed = true + for key, q := range c.writeQueueMap { + close(q.queue) + delete(c.writeQueueMap, key) + } } func (c *xicmpConnServer) sendLoop() { @@ -305,27 +315,27 @@ func (c *xicmpConnServer) sendLoop() { func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, net.ErrClosed + return 0, nil, io.EOF } - n = copy(p, packet.p) - if n != len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", n, " ", len(packet.p)) - return n, packet.addr, io.ErrShortBuffer + if len(p) < len(packet.p) { + errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + return 0, packet.addr, nil } - return n, packet.addr, nil + copy(p, packet.p) + return len(packet.p), packet.addr, nil } func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { q := c.ensureQueue(addr) if q == nil { - return 0, errors.New("xicmp closed") + return 0, io.ErrClosedPipe } c.mutex.Lock() defer c.mutex.Unlock() if c.closed { - return 0, errors.New("xicmp closed") + return 0, io.ErrClosedPipe } buf := make([]byte, len(p)) @@ -335,24 +345,13 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { case q.queue <- buf: return len(p), nil default: - return 0, errors.New("xicmp queue full") + errors.LogDebug(context.Background(), addr, " mask write err queue full") + return 0, nil } } func (c *xicmpConnServer) 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) - delete(c.writeQueueMap, key) - } - _ = c.icmpConn.Close() return c.conn.Close() } From cd81483fef02b6aba90bd49fa681b02bb8d8ae76 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 22 Feb 2026 16:31:24 +0800 Subject: [PATCH 54/75] fix possible deadlock --- transport/internet/hysteria/conn.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transport/internet/hysteria/conn.go b/transport/internet/hysteria/conn.go index be4b0f595cb5..e7eabd31734f 100644 --- a/transport/internet/hysteria/conn.go +++ b/transport/internet/hysteria/conn.go @@ -40,9 +40,11 @@ func (i *interConn) Write(b []byte) (int, error) { buf = append(buf, b...) _, err := i.stream.Write(buf) if err != nil { + i.mutex.Unlock() return 0, err } i.client = false + i.mutex.Unlock() return len(b), nil } i.mutex.Unlock() From 48cd174b56cd7b66f542272cf51aa40f346e302b Mon Sep 17 00:00:00 2001 From: null Date: Sun, 22 Feb 2026 18:31:27 +0800 Subject: [PATCH 55/75] defer instead --- transport/internet/finalmask/xdns/client.go | 2 +- transport/internet/finalmask/xdns/server.go | 2 +- transport/internet/hysteria/conn.go | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 7decc149dd3b..906138088549 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -80,7 +80,7 @@ func (c *xdnsConnClient) recvLoop() { } n, addr, err := c.PacketConn.ReadFrom(buf[:]) - if err != nil { + if err != nil || n == 0 { if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) { break } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 0da67365baf2..dc579e58eac5 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -159,7 +159,7 @@ func (c *xdnsConnServer) recvLoop() { } n, addr, err := c.PacketConn.ReadFrom(buf[:]) - if err != nil { + if err != nil || n == 0 { if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.EOF) { break } diff --git a/transport/internet/hysteria/conn.go b/transport/internet/hysteria/conn.go index e7eabd31734f..cf0920d8a9c3 100644 --- a/transport/internet/hysteria/conn.go +++ b/transport/internet/hysteria/conn.go @@ -34,20 +34,18 @@ func (i *interConn) Read(b []byte) (int, error) { func (i *interConn) Write(b []byte) (int, error) { if i.client { i.mutex.Lock() + defer i.mutex.Unlock() if i.client { buf := make([]byte, 0, quicvarint.Len(FrameTypeTCPRequest)+len(b)) buf = quicvarint.Append(buf, FrameTypeTCPRequest) buf = append(buf, b...) _, err := i.stream.Write(buf) if err != nil { - i.mutex.Unlock() return 0, err } i.client = false - i.mutex.Unlock() return len(b), nil } - i.mutex.Unlock() } return i.stream.Write(b) From 59a1a309703edd432e4b23ecf1d1c26fcc979abc Mon Sep 17 00:00:00 2001 From: null Date: Wed, 25 Feb 2026 13:40:17 +0800 Subject: [PATCH 56/75] io.ErrShortWrite --- transport/internet/finalmask/header/custom/udp.go | 5 +++-- transport/internet/finalmask/header/dns/conn.go | 3 ++- transport/internet/finalmask/header/dtls/conn.go | 3 ++- transport/internet/finalmask/header/srtp/conn.go | 3 ++- transport/internet/finalmask/header/utp/conn.go | 3 ++- transport/internet/finalmask/header/wechat/conn.go | 3 ++- transport/internet/finalmask/header/wireguard/conn.go | 3 ++- transport/internet/finalmask/mkcp/aes128gcm/conn.go | 3 ++- transport/internet/finalmask/mkcp/original/conn.go | 3 ++- transport/internet/finalmask/salamander/conn.go | 3 ++- transport/internet/finalmask/xdns/client.go | 6 +++--- transport/internet/finalmask/xdns/server.go | 4 ++-- transport/internet/finalmask/xicmp/client.go | 6 +++--- transport/internet/finalmask/xicmp/server.go | 4 ++-- 14 files changed, 31 insertions(+), 21 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index efecf90b123d..da2d868354ef 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/rand" + "io" "net" "github.com/xtls/xray-core/common" @@ -109,7 +110,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte @@ -228,7 +229,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index a60aa2e4f571..0170c3493c47 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -3,6 +3,7 @@ package dns import ( "context" "encoding/binary" + "io" "net" "github.com/xtls/xray-core/common/dice" @@ -154,7 +155,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index 0f7c16e8e386..ae7c977dbfdc 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -2,6 +2,7 @@ package dtls import ( "context" + "io" "net" "github.com/xtls/xray-core/common/dice" @@ -91,7 +92,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index 8dabcd5f0485..e8e461466d9e 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -3,6 +3,7 @@ package srtp import ( "context" "encoding/binary" + "io" "net" "github.com/xtls/xray-core/common/dice" @@ -75,7 +76,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index 7647e0490f3f..ec26b730217d 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -3,6 +3,7 @@ package utp import ( "context" "encoding/binary" + "io" "net" "github.com/xtls/xray-core/common/dice" @@ -77,7 +78,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index 157c1947112f..83228cd358a0 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -3,6 +3,7 @@ package wechat import ( "context" "encoding/binary" + "io" "net" "github.com/xtls/xray-core/common/dice" @@ -81,7 +82,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 2d310d282f36..4940b018c9d6 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -2,6 +2,7 @@ package wireguard import ( "context" + "io" "net" "github.com/xtls/xray-core/common/errors" @@ -68,7 +69,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 89fd90317b05..09488a6040c9 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "crypto/rand" "crypto/sha256" + "io" "net" "github.com/xtls/xray-core/common" @@ -91,7 +92,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index 898541bdc649..c45ca4b3257c 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "encoding/binary" "hash/fnv" + "io" "net" "github.com/xtls/xray-core/common" @@ -127,7 +128,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index f693ef07ad62..44783c840b68 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -2,6 +2,7 @@ package salamander import ( "context" + "io" "net" "github.com/xtls/xray-core/common/errors" @@ -60,7 +61,7 @@ func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if smSaltLen+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize) - return 0, nil + return 0, io.ErrShortWrite } var buf []byte diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 906138088549..1fe2448cf083 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -193,7 +193,7 @@ func (c *xdnsConnClient) sendLoop() { func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) @@ -214,7 +214,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := encode(p, c.clientID, c.domain) if err != nil { errors.LogDebug(context.Background(), addr, " mask write err ", err) - return 0, nil + return 0, io.ErrShortWrite } select { @@ -225,7 +225,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, nil + return 0, io.ErrShortWrite } } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index dc579e58eac5..e3b1dfcb30b9 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -323,7 +323,7 @@ func (c *xdnsConnServer) sendLoop() { func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) @@ -354,7 +354,7 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, nil + return 0, io.ErrShortWrite } } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 9d5dbbf84ca1..84d77a659db9 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -289,7 +289,7 @@ func (c *xicmpConnClient) sendLoop() { func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) @@ -303,7 +303,7 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := c.encode(p) if err != nil { errors.LogDebug(context.Background(), addr, " mask write err ", err) - return 0, nil + return 0, io.ErrShortWrite } c.mutex.Lock() @@ -321,7 +321,7 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, nil + return 0, io.ErrShortWrite } } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index e249d793f37a..f5ae427fd8ed 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -315,7 +315,7 @@ func (c *xicmpConnServer) sendLoop() { func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { packet, ok := <-c.readQueue if !ok { - return 0, nil, io.EOF + return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) @@ -346,7 +346,7 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, nil + return 0, io.ErrShortWrite } } From 7ddbfe159196a7fd4e199b607037e44e55c6814c Mon Sep 17 00:00:00 2001 From: null Date: Thu, 26 Feb 2026 14:46:46 +0800 Subject: [PATCH 57/75] Refactor tcp mask manager --- proxy/proxy.go | 4 +-- transport/internet/finalmask/finalmask.go | 37 ++++++++++++++++++++--- transport/internet/grpc/dial.go | 9 ++++++ transport/internet/grpc/hub.go | 4 +++ transport/internet/httpupgrade/dialer.go | 9 ++++++ transport/internet/httpupgrade/hub.go | 4 +++ transport/internet/splithttp/dialer.go | 9 ++++++ transport/internet/splithttp/hub.go | 4 +++ transport/internet/tcp/hub.go | 21 +++---------- transport/internet/websocket/dialer.go | 26 +++++++++++++++- transport/internet/websocket/hub.go | 4 +++ 11 files changed, 106 insertions(+), 25 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 246a5be956fe..49bc4b4255e4 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -790,9 +790,9 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { iConn := stat.TryUnwrapStatsConn(conn) + iConn = finalmask.UnwrapTcpMask(iConn) _, ok1 := iConn.(*proxyproto.Conn) _, ok2 := iConn.(*net.TCPConn) _, ok3 := iConn.(*internet.UnixConnWrapper) - ok4 := finalmask.SpliceAble(iConn) - return ok1 || ok2 || ok3 || ok4 + return ok1 || ok2 || ok3 } diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 68bee206e958..6856d6cc8cd6 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -1,7 +1,10 @@ package finalmask import ( + "context" "net" + + "github.com/xtls/xray-core/common/errors" ) const ( @@ -86,17 +89,41 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) { return raw, nil } +type Listener struct { + m *TcpmaskManager + net.Listener +} + +func (l *Listener) Accept() (net.Conn, error) { + conn, err := l.Listener.Accept() + if err != nil { + return nil, err + } + + newConn, err := l.m.WrapConnServer(conn) + if err != nil { + errors.LogDebugInner(context.Background(), err, "mask err") + conn.Close() + return nil, err + } + + conn = newConn + return conn, nil +} + +func (m *TcpmaskManager) WrapConnListener(listener net.Listener) (net.Listener, error) { + return &Listener{ + m: m, + Listener: listener, + }, nil +} + type TcpMaskConn interface { TcpMaskConn() RawConn() net.Conn Splice() bool } -func SpliceAble(conn net.Conn) bool { - _, ok := UnwrapTcpMask(conn).(*net.TCPConn) - return ok -} - func UnwrapTcpMask(conn net.Conn) net.Conn { for { if v, ok := conn.(TcpMaskConn); ok { diff --git a/transport/internet/grpc/dial.go b/transport/internet/grpc/dial.go index e0b0aa03f8a7..0decaf9b2e7a 100644 --- a/transport/internet/grpc/dial.go +++ b/transport/internet/grpc/dial.go @@ -125,6 +125,15 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt) if err == nil { + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(c) + if err != nil { + c.Close() + return nil, errors.New("mask err").Base(err) + } + c = newConn + } + if tlsConfig != nil { config := tlsConfig.GetTLSConfig() if config.ServerName == "" && address.Family().IsDomain() { diff --git a/transport/internet/grpc/hub.go b/transport/internet/grpc/hub.go index ae8788fab5c8..810c9d5a75cb 100644 --- a/transport/internet/grpc/hub.go +++ b/transport/internet/grpc/hub.go @@ -120,6 +120,10 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i } } + if settings.TcpmaskManager != nil { + streamListener, _ = settings.TcpmaskManager.WrapConnListener(streamListener) + } + errors.LogDebug(ctx, "gRPC listen for service name `"+grpcSettings.getServiceName()+"` tun `"+grpcSettings.getTunStreamName()+"` multi tun `"+grpcSettings.getTunMultiStreamName()+"`") encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.getServiceName(), grpcSettings.getTunStreamName(), grpcSettings.getTunMultiStreamName()) diff --git a/transport/internet/httpupgrade/dialer.go b/transport/internet/httpupgrade/dialer.go index 4d718eb8036e..eacbded4edb2 100644 --- a/transport/internet/httpupgrade/dialer.go +++ b/transport/internet/httpupgrade/dialer.go @@ -52,6 +52,15 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings * return nil, err } + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(pconn) + if err != nil { + pconn.Close() + return nil, errors.New("mask err").Base(err) + } + pconn = newConn + } + var conn net.Conn var requestURL url.URL tConfig := tls.ConfigFromStreamSettings(streamSettings) diff --git a/transport/internet/httpupgrade/hub.go b/transport/internet/httpupgrade/hub.go index 778a35e5e0e0..7380e38a5182 100644 --- a/transport/internet/httpupgrade/hub.go +++ b/transport/internet/httpupgrade/hub.go @@ -142,6 +142,10 @@ func ListenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, errors.LogInfo(ctx, "listening TCP(for HttpUpgrade) on ", address, ":", port) } + if streamSettings.TcpmaskManager != nil { + listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) + } + if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { errors.LogWarning(ctx, "accepting PROXY protocol") } diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index ffb501497a17..6f39c20daaf0 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -117,6 +117,15 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea return nil, err } + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(conn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = newConn + } + if realityConfig != nil { return reality.UClient(conn, realityConfig, ctxInner, dest) } diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index fe2a1822a692..3efcb85f95ce 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -509,6 +509,10 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet errors.LogInfo(ctx, "listening TCP for XHTTP on ", address, ":", port) } + if !l.isH3 && streamSettings.TcpmaskManager != nil { + l.listener, _ = streamSettings.TcpmaskManager.WrapConnListener(l.listener) + } + // tcp/unix (h1/h2) if l.listener != nil { if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index cb3e55e407b4..290c51073ecf 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -11,7 +11,6 @@ import ( "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/transport/internet" - "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" @@ -25,17 +24,12 @@ type Listener struct { authConfig internet.ConnectionAuthenticator config *Config addConn internet.ConnHandler - - isTcp bool - tcpMaskManager *finalmask.TcpmaskManager } // ListenTCP creates a new Listener based on configurations. func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { l := &Listener{ addConn: handler, - - tcpMaskManager: streamSettings.TcpmaskManager, } tcpSettings := streamSettings.ProtocolSettings.(*Config) l.config = tcpSettings @@ -68,7 +62,10 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe return nil, errors.New("failed to listen TCP on ", address, ":", port).Base(err) } errors.LogInfo(ctx, "listening TCP on ", address, ":", port) - l.isTcp = true + } + + if streamSettings.TcpmaskManager != nil { + listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { @@ -116,16 +113,6 @@ func (v *Listener) keepAccepting() { continue } - if v.isTcp && v.tcpMaskManager != nil { - newConn, err := v.tcpMaskManager.WrapConnServer(conn) - if err != nil { - errors.LogDebug(context.Background(), errors.New("mask err").Base(err)) - conn.Close() - continue - } - conn = newConn - } - go func() { if v.tlsConfig != nil { conn = tls.Server(conn, v.tlsConfig) diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 5e41389304a1..e5354908d77c 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -48,7 +48,21 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in dialer := &websocket.Dialer{ NetDial: func(network, addr string) (net.Conn, error) { - return internet.DialSystem(ctx, dest, streamSettings.SocketSettings) + conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) + if err != nil { + return nil, err + } + + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(conn) + if err != nil { + conn.Close() + return nil, errors.New("mask err").Base(err) + } + conn = newConn + } + + return conn, err }, ReadBufferSize: 4 * 1024, WriteBufferSize: 4 * 1024, @@ -70,6 +84,16 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in errors.LogErrorInner(ctx, err, "failed to dial to "+addr) return nil, err } + + if streamSettings.TcpmaskManager != nil { + newConn, err := streamSettings.TcpmaskManager.WrapConnClient(pconn) + if err != nil { + pconn.Close() + return nil, errors.New("mask err").Base(err) + } + pconn = newConn + } + // TLS and apply the handshake cn := tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn) if err := cn.WebsocketHandshakeContext(ctx); err != nil { diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index b47e7d581770..e85f867aa86b 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -129,6 +129,10 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet errors.LogInfo(ctx, "listening TCP(for WS) on ", address, ":", port) } + if streamSettings.TcpmaskManager != nil { + listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) + } + if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { errors.LogWarning(ctx, "accepting PROXY protocol") } From 9a2fb336879e142ea9480c3109d385decb452f14 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 26 Feb 2026 15:09:15 +0800 Subject: [PATCH 58/75] UnwrapRawConn --- proxy/proxy.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 49bc4b4255e4..acda52d9c14e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -688,6 +688,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { conn = realityUConn.NetConn() } } + + conn = finalmask.UnwrapTcpMask(conn) + if pc, ok := conn.(*proxyproto.Conn); ok { conn = pc.Raw() // 8192 > 4096, there is no need to process pc's bufReader @@ -695,7 +698,6 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { if uc, ok := conn.(*internet.UnixConnWrapper); ok { conn = uc.UnixConn } - conn = finalmask.UnwrapTcpMask(conn) } return conn, readCounter, writerCounter } From a9426cf8a1981b9be29e3054ea134a4bd018936b Mon Sep 17 00:00:00 2001 From: null Date: Thu, 26 Feb 2026 17:08:09 +0800 Subject: [PATCH 59/75] NewTcpListener --- transport/internet/finalmask/finalmask.go | 22 +++++++++++++--------- transport/internet/grpc/hub.go | 2 +- transport/internet/httpupgrade/hub.go | 2 +- transport/internet/splithttp/hub.go | 2 +- transport/internet/tcp/hub.go | 2 +- transport/internet/websocket/hub.go | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index 6856d6cc8cd6..bf64361cc1fd 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -89,12 +89,23 @@ func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) { return raw, nil } -type Listener struct { +func (m *TcpmaskManager) WrapListener(l net.Listener) (net.Listener, error) { + return NewTcpListener(m, l) +} + +type tcpListener struct { m *TcpmaskManager net.Listener } -func (l *Listener) Accept() (net.Conn, error) { +func NewTcpListener(m *TcpmaskManager, l net.Listener) (net.Listener, error) { + return &tcpListener{ + m: m, + Listener: l, + }, nil +} + +func (l *tcpListener) Accept() (net.Conn, error) { conn, err := l.Listener.Accept() if err != nil { return nil, err @@ -111,13 +122,6 @@ func (l *Listener) Accept() (net.Conn, error) { return conn, nil } -func (m *TcpmaskManager) WrapConnListener(listener net.Listener) (net.Listener, error) { - return &Listener{ - m: m, - Listener: listener, - }, nil -} - type TcpMaskConn interface { TcpMaskConn() RawConn() net.Conn diff --git a/transport/internet/grpc/hub.go b/transport/internet/grpc/hub.go index 810c9d5a75cb..34f8f1c0e60d 100644 --- a/transport/internet/grpc/hub.go +++ b/transport/internet/grpc/hub.go @@ -121,7 +121,7 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i } if settings.TcpmaskManager != nil { - streamListener, _ = settings.TcpmaskManager.WrapConnListener(streamListener) + streamListener, _ = settings.TcpmaskManager.WrapListener(streamListener) } errors.LogDebug(ctx, "gRPC listen for service name `"+grpcSettings.getServiceName()+"` tun `"+grpcSettings.getTunStreamName()+"` multi tun `"+grpcSettings.getTunMultiStreamName()+"`") diff --git a/transport/internet/httpupgrade/hub.go b/transport/internet/httpupgrade/hub.go index 7380e38a5182..8e70ad08a735 100644 --- a/transport/internet/httpupgrade/hub.go +++ b/transport/internet/httpupgrade/hub.go @@ -143,7 +143,7 @@ func ListenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, } if streamSettings.TcpmaskManager != nil { - listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) + listener, _ = streamSettings.TcpmaskManager.WrapListener(listener) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index 3efcb85f95ce..4f0832cb10ee 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -510,7 +510,7 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet } if !l.isH3 && streamSettings.TcpmaskManager != nil { - l.listener, _ = streamSettings.TcpmaskManager.WrapConnListener(l.listener) + l.listener, _ = streamSettings.TcpmaskManager.WrapListener(l.listener) } // tcp/unix (h1/h2) diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 290c51073ecf..ede97499dac9 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -65,7 +65,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe } if streamSettings.TcpmaskManager != nil { - listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) + listener, _ = streamSettings.TcpmaskManager.WrapListener(listener) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index e85f867aa86b..73799174ca24 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -130,7 +130,7 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet } if streamSettings.TcpmaskManager != nil { - listener, _ = streamSettings.TcpmaskManager.WrapConnListener(listener) + listener, _ = streamSettings.TcpmaskManager.WrapListener(listener) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { From 1920a63aac840d91e4f15b9ea47ffc2d306e036f Mon Sep 17 00:00:00 2001 From: null Date: Fri, 27 Feb 2026 10:10:18 +0800 Subject: [PATCH 60/75] tcpListener --- transport/internet/finalmask/finalmask.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/transport/internet/finalmask/finalmask.go b/transport/internet/finalmask/finalmask.go index bf64361cc1fd..3eee635a5a1c 100644 --- a/transport/internet/finalmask/finalmask.go +++ b/transport/internet/finalmask/finalmask.go @@ -108,18 +108,17 @@ func NewTcpListener(m *TcpmaskManager, l net.Listener) (net.Listener, error) { func (l *tcpListener) Accept() (net.Conn, error) { conn, err := l.Listener.Accept() if err != nil { - return nil, err + return conn, err } newConn, err := l.m.WrapConnServer(conn) if err != nil { errors.LogDebugInner(context.Background(), err, "mask err") - conn.Close() - return nil, err + // conn.Close() + return conn, nil } - conn = newConn - return conn, nil + return newConn, nil } type TcpMaskConn interface { From b5e5543e01627c5da34a3516bd353534833e255a Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 21:12:44 +0800 Subject: [PATCH 61/75] kcp AckList --- transport/internet/kcp/receiving.go | 10 ++++++---- transport/internet/kcp/segment.go | 15 +++++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/transport/internet/kcp/receiving.go b/transport/internet/kcp/receiving.go index a75014aff789..7dae510fcac0 100644 --- a/transport/internet/kcp/receiving.go +++ b/transport/internet/kcp/receiving.go @@ -47,9 +47,11 @@ type AckList struct { flushCandidates []uint32 dirty bool + + mss uint32 } -func NewAckList(writer SegmentWriter) *AckList { +func NewAckList(writer SegmentWriter, mss uint32) *AckList { return &AckList{ writer: writer, timestamps: make([]uint32, 0, 128), @@ -90,7 +92,7 @@ func (l *AckList) Clear(una uint32) { func (l *AckList) Flush(current uint32, rto uint32) { l.flushCandidates = l.flushCandidates[:0] - seg := NewAckSegment() + seg := NewAckSegment((int(l.mss) - 17) / 4) for i := 0; i < len(l.numbers); i++ { if l.nextFlush[i] > current { if len(l.flushCandidates) < cap(l.flushCandidates) { @@ -109,7 +111,7 @@ func (l *AckList) Flush(current uint32, rto uint32) { if seg.IsFull() { l.writer.Write(seg) seg.Release() - seg = NewAckSegment() + seg = NewAckSegment((int(l.mss) - 17) / 4) l.dirty = false } } @@ -144,7 +146,7 @@ func NewReceivingWorker(kcp *Connection) *ReceivingWorker { window: NewReceivingWindow(), windowSize: kcp.Config.GetReceivingInFlightSize(), } - worker.acklist = NewAckList(worker) + worker.acklist = NewAckList(worker, kcp.mss) return worker } diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index b97d25ea9366..a9c77a97551f 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -131,12 +131,19 @@ type AckSegment struct { ReceivingNext uint32 Timestamp uint32 NumberList []uint32 + + limit int } const ackNumberLimit = 128 -func NewAckSegment() *AckSegment { - return new(AckSegment) +func NewAckSegment(limit int) *AckSegment { + if limit > ackNumberLimit { + limit = ackNumberLimit + } + return &AckSegment{ + limit: limit, + } } func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { @@ -188,7 +195,7 @@ func (s *AckSegment) PutNumber(number uint32) { } func (s *AckSegment) IsFull() bool { - return len(s.NumberList) == ackNumberLimit + return len(s.NumberList) == s.limit } func (s *AckSegment) IsEmpty() bool { @@ -290,7 +297,7 @@ func ReadSegment(buf []byte) (Segment, []byte) { case CommandData: seg = NewDataSegment() case CommandACK: - seg = NewAckSegment() + seg = NewAckSegment(128) default: seg = NewCmdOnlySegment() } From 21df8f3561448f6011876e3d7b092df9cdeec8e7 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 21:15:04 +0800 Subject: [PATCH 62/75] limit = 1 --- transport/internet/kcp/segment.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index a9c77a97551f..5938f447eec4 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -138,6 +138,9 @@ type AckSegment struct { const ackNumberLimit = 128 func NewAckSegment(limit int) *AckSegment { + if limit == 0 { + limit = 1 + } if limit > ackNumberLimit { limit = ackNumberLimit } From 466ce529fd45f96de2bbd91ba4605d3b83fca391 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 21:18:45 +0800 Subject: [PATCH 63/75] 1 --- transport/internet/kcp/receiving.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/internet/kcp/receiving.go b/transport/internet/kcp/receiving.go index 7dae510fcac0..5a32d6d816a4 100644 --- a/transport/internet/kcp/receiving.go +++ b/transport/internet/kcp/receiving.go @@ -146,7 +146,7 @@ func NewReceivingWorker(kcp *Connection) *ReceivingWorker { window: NewReceivingWindow(), windowSize: kcp.Config.GetReceivingInFlightSize(), } - worker.acklist = NewAckList(worker, kcp.mss) + worker.acklist = NewAckList(worker, kcp.mss+DataSegmentOverhead) return worker } From 9424456425dedd47aa5af2262e92bdb4c9657b65 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 21:50:26 +0800 Subject: [PATCH 64/75] fix bug --- transport/internet/finalmask/xdns/client.go | 4 ++-- transport/internet/finalmask/xdns/server.go | 2 +- transport/internet/kcp/segment.go | 8 ++++---- transport/internet/kcp/segment_test.go | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 1fe2448cf083..a6c7c217a7fe 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -60,7 +60,7 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { pollChan: make(chan struct{}, pollLimit), readQueue: make(chan *packet, 128), - writeQueue: make(chan *packet, 128), + writeQueue: make(chan *packet, 256), } rand.Read(conn.clientID) @@ -213,7 +213,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := encode(p, c.clientID, c.domain) if err != nil { - errors.LogDebug(context.Background(), addr, " mask write err ", err) + errors.LogDebug(context.Background(), addr, " mask write err ", err, " ", len(p)) return 0, io.ErrShortWrite } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index e3b1dfcb30b9..6ef63ee608fe 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -126,7 +126,7 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { q, ok := c.writeQueueMap[addr.String()] if !ok { q = &queue{ - queue: make(chan []byte, 128), + queue: make(chan []byte, 512), stash: make(chan []byte, 1), } c.writeQueueMap[addr.String()] = q diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index 5938f447eec4..2beaa0b67275 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -132,20 +132,20 @@ type AckSegment struct { Timestamp uint32 NumberList []uint32 - limit int + Limit int } const ackNumberLimit = 128 func NewAckSegment(limit int) *AckSegment { - if limit == 0 { + if limit <= 0 { limit = 1 } if limit > ackNumberLimit { limit = ackNumberLimit } return &AckSegment{ - limit: limit, + Limit: limit, } } @@ -198,7 +198,7 @@ func (s *AckSegment) PutNumber(number uint32) { } func (s *AckSegment) IsFull() bool { - return len(s.NumberList) == s.limit + return len(s.NumberList) == s.Limit } func (s *AckSegment) IsEmpty() bool { diff --git a/transport/internet/kcp/segment_test.go b/transport/internet/kcp/segment_test.go index daa9098a77c6..cc12ea9bd03a 100644 --- a/transport/internet/kcp/segment_test.go +++ b/transport/internet/kcp/segment_test.go @@ -71,6 +71,7 @@ func TestACKSegment(t *testing.T) { ReceivingNext: 3, Timestamp: 10, NumberList: []uint32{1, 3, 5, 7, 9}, + Limit: 128, } nBytes := seg.ByteSize() From 03340f29e46583cc56e12174ffd9e93071dff64a Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 22:36:07 +0800 Subject: [PATCH 65/75] nil err for temp err --- transport/internet/finalmask/xdns/client.go | 4 ++-- transport/internet/finalmask/xdns/server.go | 2 +- transport/internet/finalmask/xicmp/client.go | 4 ++-- transport/internet/finalmask/xicmp/server.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index a6c7c217a7fe..86c4670e3477 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -214,7 +214,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := encode(p, c.clientID, c.domain) if err != nil { errors.LogDebug(context.Background(), addr, " mask write err ", err, " ", len(p)) - return 0, io.ErrShortWrite + return 0, nil } select { @@ -225,7 +225,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, io.ErrShortWrite + return 0, nil } } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 6ef63ee608fe..1c1981c2bab3 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -354,7 +354,7 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, io.ErrShortWrite + return 0, nil } } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 84d77a659db9..a18efc4481c7 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -303,7 +303,7 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := c.encode(p) if err != nil { errors.LogDebug(context.Background(), addr, " mask write err ", err) - return 0, io.ErrShortWrite + return 0, nil } c.mutex.Lock() @@ -321,7 +321,7 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, io.ErrShortWrite + return 0, nil } } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index f5ae427fd8ed..8ba4bb262dfe 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -346,7 +346,7 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil default: errors.LogDebug(context.Background(), addr, " mask write err queue full") - return 0, io.ErrShortWrite + return 0, nil } } From 988ef9e1bf1b6912a6abd0fbd5a4a6fbe0d75424 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 23:26:06 +0800 Subject: [PATCH 66/75] fix bug --- transport/internet/kcp/receiving.go | 1 + 1 file changed, 1 insertion(+) diff --git a/transport/internet/kcp/receiving.go b/transport/internet/kcp/receiving.go index 5a32d6d816a4..3d1fc01438f1 100644 --- a/transport/internet/kcp/receiving.go +++ b/transport/internet/kcp/receiving.go @@ -58,6 +58,7 @@ func NewAckList(writer SegmentWriter, mss uint32) *AckList { numbers: make([]uint32, 0, 128), nextFlush: make([]uint32, 0, 128), flushCandidates: make([]uint32, 0, 128), + mss: mss, } } From 3c0cb1d2959e57bd355105ad4ede24f0f3207658 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 2 Mar 2026 23:34:58 +0800 Subject: [PATCH 67/75] all nil --- transport/internet/finalmask/header/custom/udp.go | 5 ++--- transport/internet/finalmask/header/dns/conn.go | 3 +-- transport/internet/finalmask/header/dtls/conn.go | 3 +-- transport/internet/finalmask/header/srtp/conn.go | 3 +-- transport/internet/finalmask/header/utp/conn.go | 3 +-- transport/internet/finalmask/header/wechat/conn.go | 3 +-- transport/internet/finalmask/header/wireguard/conn.go | 3 +-- transport/internet/finalmask/mkcp/aes128gcm/conn.go | 3 +-- transport/internet/finalmask/mkcp/original/conn.go | 3 +-- transport/internet/finalmask/salamander/conn.go | 3 +-- 10 files changed, 11 insertions(+), 21 deletions(-) diff --git a/transport/internet/finalmask/header/custom/udp.go b/transport/internet/finalmask/header/custom/udp.go index da2d868354ef..efecf90b123d 100644 --- a/transport/internet/finalmask/header/custom/udp.go +++ b/transport/internet/finalmask/header/custom/udp.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "crypto/rand" - "io" "net" "github.com/xtls/xray-core/common" @@ -110,7 +109,7 @@ func (c *udpCustomClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte @@ -229,7 +228,7 @@ func (c *udpCustomServerConn) ReadFrom(p []byte) (n int, addr net.Addr, err erro func (c *udpCustomServerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(c.header.merged)+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", len(c.header.merged)+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/dns/conn.go b/transport/internet/finalmask/header/dns/conn.go index 0170c3493c47..a60aa2e4f571 100644 --- a/transport/internet/finalmask/header/dns/conn.go +++ b/transport/internet/finalmask/header/dns/conn.go @@ -3,7 +3,6 @@ package dns import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -155,7 +154,7 @@ func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/dtls/conn.go b/transport/internet/finalmask/header/dtls/conn.go index ae7c977dbfdc..0f7c16e8e386 100644 --- a/transport/internet/finalmask/header/dtls/conn.go +++ b/transport/internet/finalmask/header/dtls/conn.go @@ -2,7 +2,6 @@ package dtls import ( "context" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -92,7 +91,7 @@ func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/srtp/conn.go b/transport/internet/finalmask/header/srtp/conn.go index e8e461466d9e..8dabcd5f0485 100644 --- a/transport/internet/finalmask/header/srtp/conn.go +++ b/transport/internet/finalmask/header/srtp/conn.go @@ -3,7 +3,6 @@ package srtp import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -76,7 +75,7 @@ func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/utp/conn.go b/transport/internet/finalmask/header/utp/conn.go index ec26b730217d..7647e0490f3f 100644 --- a/transport/internet/finalmask/header/utp/conn.go +++ b/transport/internet/finalmask/header/utp/conn.go @@ -3,7 +3,6 @@ package utp import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -78,7 +77,7 @@ func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/wechat/conn.go b/transport/internet/finalmask/header/wechat/conn.go index 83228cd358a0..157c1947112f 100644 --- a/transport/internet/finalmask/header/wechat/conn.go +++ b/transport/internet/finalmask/header/wechat/conn.go @@ -3,7 +3,6 @@ package wechat import ( "context" "encoding/binary" - "io" "net" "github.com/xtls/xray-core/common/dice" @@ -82,7 +81,7 @@ func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/header/wireguard/conn.go b/transport/internet/finalmask/header/wireguard/conn.go index 4940b018c9d6..2d310d282f36 100644 --- a/transport/internet/finalmask/header/wireguard/conn.go +++ b/transport/internet/finalmask/header/wireguard/conn.go @@ -2,7 +2,6 @@ package wireguard import ( "context" - "io" "net" "github.com/xtls/xray-core/common/errors" @@ -69,7 +68,7 @@ func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.header.Size()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.header.Size()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/mkcp/aes128gcm/conn.go b/transport/internet/finalmask/mkcp/aes128gcm/conn.go index 09488a6040c9..89fd90317b05 100644 --- a/transport/internet/finalmask/mkcp/aes128gcm/conn.go +++ b/transport/internet/finalmask/mkcp/aes128gcm/conn.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" "crypto/rand" "crypto/sha256" - "io" "net" "github.com/xtls/xray-core/common" @@ -92,7 +91,7 @@ func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.NonceSize()+c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.NonceSize()+c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/mkcp/original/conn.go b/transport/internet/finalmask/mkcp/original/conn.go index c45ca4b3257c..898541bdc649 100644 --- a/transport/internet/finalmask/mkcp/original/conn.go +++ b/transport/internet/finalmask/mkcp/original/conn.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" "encoding/binary" "hash/fnv" - "io" "net" "github.com/xtls/xray-core/common" @@ -128,7 +127,7 @@ func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if c.aead.Overhead()+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", c.aead.Overhead()+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte diff --git a/transport/internet/finalmask/salamander/conn.go b/transport/internet/finalmask/salamander/conn.go index 44783c840b68..f693ef07ad62 100644 --- a/transport/internet/finalmask/salamander/conn.go +++ b/transport/internet/finalmask/salamander/conn.go @@ -2,7 +2,6 @@ package salamander import ( "context" - "io" "net" "github.com/xtls/xray-core/common/errors" @@ -61,7 +60,7 @@ func (c *salamanderConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *salamanderConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if smSaltLen+len(p) > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err short write ", smSaltLen+len(p), " ", finalmask.UDPSize) - return 0, io.ErrShortWrite + return 0, nil } var buf []byte From 0af9817bcb93be50e17197b226a36ef8875228c0 Mon Sep 17 00:00:00 2001 From: null Date: Tue, 3 Mar 2026 11:30:50 +0800 Subject: [PATCH 68/75] fix crash & enhance xdns --- transport/internet/finalmask/xdns/client.go | 5 ++- transport/internet/finalmask/xdns/dns_test.go | 2 + transport/internet/finalmask/xdns/server.go | 43 ++++++++++--------- transport/internet/finalmask/xicmp/server.go | 15 +++---- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index 86c4670e3477..b87aaf305560 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet/finalmask" ) @@ -60,10 +61,10 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { pollChan: make(chan struct{}, pollLimit), readQueue: make(chan *packet, 128), - writeQueue: make(chan *packet, 256), + writeQueue: make(chan *packet, 512), } - rand.Read(conn.clientID) + common.Must2(rand.Read(conn.clientID)) go conn.recvLoop() go conn.sendLoop() diff --git a/transport/internet/finalmask/xdns/dns_test.go b/transport/internet/finalmask/xdns/dns_test.go index b07f57b9758c..aa163476d9f1 100644 --- a/transport/internet/finalmask/xdns/dns_test.go +++ b/transport/internet/finalmask/xdns/dns_test.go @@ -557,6 +557,8 @@ func TestEncodeRDataTXT(t *testing.T) { if len(encoded) > 256 { t.Errorf("EncodeRDataTXT(%d bytes) returned %d bytes", len(p), len(encoded)) } + + fmt.Println(EncodeRDataTXT(nil)) } func TestRDataTXTRoundTrip(t *testing.T) { diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 1c1981c2bab3..cb30d36cc0fa 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -15,7 +15,7 @@ import ( ) const ( - idleTimeout = 2 * time.Minute + idleTimeout = 6 * time.Second responseTTL = 60 maxResponseDelay = 1 * time.Second ) @@ -73,7 +73,7 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { domain: domain, - ch: make(chan *record, 100), + ch: make(chan *record, 500), readQueue: make(chan *packet, 128), writeQueueMap: make(map[string]*queue), } @@ -116,9 +116,6 @@ func (c *xdnsConnServer) clean() { } func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.closed { return nil } @@ -131,7 +128,7 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { } c.writeQueueMap[addr.String()] = q } - q.last = time.Now() + // q.last = time.Now() return q } @@ -204,6 +201,7 @@ func (c *xdnsConnServer) recvLoop() { select { case c.ch <- &record{resp, addr, clientIDToAddr(clientID)}: default: + errors.LogDebug(context.Background(), "mask read err record queue full") } } } @@ -251,23 +249,31 @@ func (c *xdnsConnServer) sendLoop() { limit := maxEncodedPayload timer := time.NewTimer(maxResponseDelay) for { - queue := c.ensureQueue(rec.ClientAddr) - if queue == nil { + c.mutex.Lock() + q := c.ensureQueue(rec.ClientAddr) + if q == nil { + c.mutex.Unlock() return } + c.mutex.Unlock() var p []byte select { - case p = <-queue.stash: + case p = <-q.stash: + q.last = time.Now() default: select { - case p = <-queue.stash: - case p = <-queue.queue: + case p = <-q.stash: + q.last = time.Now() + case p = <-q.queue: + q.last = time.Now() default: select { - case p = <-queue.stash: - case p = <-queue.queue: + case p = <-q.stash: + q.last = time.Now() + case p = <-q.queue: + q.last = time.Now() case <-timer.C: case nextRec = <-c.ch: } @@ -284,7 +290,7 @@ func (c *xdnsConnServer) sendLoop() { if payload.Len() == 0 { } else if limit < 0 { - c.stash(queue, p) + c.stash(q, p) break } @@ -296,6 +302,7 @@ func (c *xdnsConnServer) sendLoop() { _ = binary.Write(&payload, binary.BigEndian, uint16(len(p))) payload.Write(p) } + timer.Stop() rec.Resp.Answer[0].Data = EncodeRDataTXT(payload.Bytes()) @@ -334,15 +341,11 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { - q := c.ensureQueue(addr) - if q == nil { - return 0, io.ErrClosedPipe - } - c.mutex.Lock() defer c.mutex.Unlock() - if c.closed { + q := c.ensureQueue(addr) + if q == nil { return 0, io.ErrClosedPipe } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 8ba4bb262dfe..d5f49cc14b7b 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -119,9 +119,6 @@ func (c *xicmpConnServer) clean() { } func (c *xicmpConnServer) ensureQueue(addr net.Addr) *queue { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.closed { return nil } @@ -241,6 +238,7 @@ func (c *xicmpConnServer) recvLoop() { }, }: default: + errors.LogDebug(context.Background(), "mask read err record queue full") } } @@ -271,10 +269,13 @@ func (c *xicmpConnServer) sendLoop() { } } + c.mutex.Lock() queue := c.ensureQueue(rec.addr) if queue == nil { + c.mutex.Unlock() return } + c.mutex.Unlock() var p []byte @@ -326,15 +327,11 @@ func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { - q := c.ensureQueue(addr) - if q == nil { - return 0, io.ErrClosedPipe - } - c.mutex.Lock() defer c.mutex.Unlock() - if c.closed { + q := c.ensureQueue(addr) + if q == nil { return 0, io.ErrClosedPipe } From f9bc2da334f935eec049323ecc14738ca87e013e Mon Sep 17 00:00:00 2001 From: null Date: Tue, 3 Mar 2026 13:05:13 +0800 Subject: [PATCH 69/75] q.last --- transport/internet/finalmask/xdns/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index cb30d36cc0fa..0f5b5791eacf 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -127,6 +127,7 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { stash: make(chan []byte, 1), } c.writeQueueMap[addr.String()] = q + q.last = time.Now() } // q.last = time.Now() From 8ab1b9d0eb9570dd5dd781679a21dea0792018cc Mon Sep 17 00:00:00 2001 From: null Date: Tue, 3 Mar 2026 17:00:38 +0800 Subject: [PATCH 70/75] 10 * time.Second --- transport/internet/finalmask/xdns/client.go | 3 ++- transport/internet/finalmask/xdns/server.go | 13 ++++--------- transport/internet/finalmask/xicmp/client.go | 3 ++- transport/internet/finalmask/xicmp/server.go | 15 ++++++++------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index b87aaf305560..d84cf2c045f5 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -61,7 +61,7 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) { pollChan: make(chan struct{}, pollLimit), readQueue: make(chan *packet, 128), - writeQueue: make(chan *packet, 512), + writeQueue: make(chan *packet, 256), } common.Must2(rand.Read(conn.clientID)) @@ -112,6 +112,7 @@ func (c *xdnsConnClient) recvLoop() { addr: addr, }: default: + errors.LogDebug(context.Background(), "mask read err queue full") } } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 0f5b5791eacf..8d5bc5a872e3 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -15,7 +15,7 @@ import ( ) const ( - idleTimeout = 6 * time.Second + idleTimeout = 10 * time.Second responseTTL = 60 maxResponseDelay = 1 * time.Second ) @@ -74,7 +74,7 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) { domain: domain, ch: make(chan *record, 500), - readQueue: make(chan *packet, 128), + readQueue: make(chan *packet, 256), writeQueueMap: make(map[string]*queue), } @@ -127,9 +127,8 @@ func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue { stash: make(chan []byte, 1), } c.writeQueueMap[addr.String()] = q - q.last = time.Now() } - // q.last = time.Now() + q.last = time.Now() return q } @@ -190,6 +189,7 @@ func (c *xdnsConnServer) recvLoop() { addr: clientIDToAddr(clientID), }: default: + errors.LogDebug(context.Background(), "mask read err queue full") } } } else { @@ -262,19 +262,14 @@ func (c *xdnsConnServer) sendLoop() { select { case p = <-q.stash: - q.last = time.Now() default: select { case p = <-q.stash: - q.last = time.Now() case p = <-q.queue: - q.last = time.Now() default: select { case p = <-q.stash: - q.last = time.Now() case p = <-q.queue: - q.last = time.Now() case <-timer.C: case nextRec = <-c.ch: } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index a18efc4481c7..04dedc8397df 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -91,7 +91,7 @@ func NewConnClient(c *Config, raw net.PacketConn, level int) (net.PacketConn, er pollChan: make(chan struct{}, pollLimit), readQueue: make(chan *packet, 128), - writeQueue: make(chan *packet, 128), + writeQueue: make(chan *packet, 256), } go conn.recvLoop() @@ -206,6 +206,7 @@ func (c *xicmpConnClient) recvLoop() { addr: &net.UDPAddr{IP: addr.(*net.IPAddr).IP}, }: default: + errors.LogDebug(context.Background(), "mask read err queue full") } select { diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index d5f49cc14b7b..bf02fe294f52 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -17,7 +17,7 @@ import ( ) const ( - idleTimeout = 2 * time.Minute + idleTimeout = 10 * time.Second maxResponseDelay = 1 * time.Second ) @@ -30,7 +30,7 @@ type record struct { } type queue struct { - lash time.Time + last time.Time queue chan []byte } @@ -77,8 +77,8 @@ func NewConnServer(c *Config, raw net.PacketConn, level int) (net.PacketConn, er proto: proto, config: c, - ch: make(chan *record, 100), - readQueue: make(chan *packet, 128), + ch: make(chan *record, 500), + readQueue: make(chan *packet, 256), writeQueueMap: make(map[string]*queue), } @@ -101,7 +101,7 @@ func (c *xicmpConnServer) clean() { now := time.Now() for key, q := range c.writeQueueMap { - if now.Sub(q.lash) >= idleTimeout { + if now.Sub(q.last) >= idleTimeout { close(q.queue) delete(c.writeQueueMap, key) } @@ -126,11 +126,11 @@ func (c *xicmpConnServer) ensureQueue(addr net.Addr) *queue { q, ok := c.writeQueueMap[addr.String()] if !ok { q = &queue{ - queue: make(chan []byte, 128), + queue: make(chan []byte, 512), } c.writeQueueMap[addr.String()] = q } - q.lash = time.Now() + q.last = time.Now() return q } @@ -223,6 +223,7 @@ func (c *xicmpConnServer) recvLoop() { }, }: default: + errors.LogDebug(context.Background(), "mask read err queue full") } } From 5907e81c4db3c4884523f813746fbed6ed7c98d7 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 4 Mar 2026 10:01:46 +0800 Subject: [PATCH 71/75] queue -> q --- transport/internet/finalmask/xicmp/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index bf02fe294f52..5eae9989a5b0 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -271,8 +271,8 @@ func (c *xicmpConnServer) sendLoop() { } c.mutex.Lock() - queue := c.ensureQueue(rec.addr) - if queue == nil { + q := c.ensureQueue(rec.addr) + if q == nil { c.mutex.Unlock() return } @@ -283,10 +283,10 @@ func (c *xicmpConnServer) sendLoop() { timer := time.NewTimer(maxResponseDelay) select { - case p = <-queue.queue: + case p = <-q.queue: default: select { - case p = <-queue.queue: + case p = <-q.queue: case <-timer.C: case nextRec = <-c.ch: } From d616f2f0cbfd1db9263b666c84973586a6c974e6 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 4 Mar 2026 16:27:44 +0800 Subject: [PATCH 72/75] more log & some logic --- transport/internet/finalmask/xdns/client.go | 15 +++++--- transport/internet/finalmask/xdns/server.go | 37 ++++++++++++-------- transport/internet/finalmask/xicmp/client.go | 10 +++--- transport/internet/finalmask/xicmp/server.go | 17 ++++++--- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/transport/internet/finalmask/xdns/client.go b/transport/internet/finalmask/xdns/client.go index d84cf2c045f5..81b1366269bc 100644 --- a/transport/internet/finalmask/xdns/client.go +++ b/transport/internet/finalmask/xdns/client.go @@ -90,6 +90,7 @@ func (c *xdnsConnClient) recvLoop() { resp, err := MessageFromWireFormat(buf[:n]) if err != nil { + errors.LogDebug(context.Background(), addr, " xdns from wireformat err ", err) continue } @@ -112,7 +113,7 @@ func (c *xdnsConnClient) recvLoop() { addr: addr, }: default: - errors.LogDebug(context.Background(), "mask read err queue full") + errors.LogDebug(context.Background(), addr, " mask read err queue full") } } @@ -124,6 +125,8 @@ func (c *xdnsConnClient) recvLoop() { } } + errors.LogDebug(context.Background(), "xdns closed") + close(c.pollChan) close(c.readQueue) @@ -187,7 +190,11 @@ func (c *xdnsConnClient) sendLoop() { } if p != nil { - _, _ = c.PacketConn.WriteTo(p.p, p.addr) + _, err := c.PacketConn.WriteTo(p.p, p.addr) + if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.ErrClosedPipe) { + c.closed = true + break + } } } } @@ -198,7 +205,7 @@ func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p)) return 0, packet.addr, nil } copy(p, packet.p) @@ -215,7 +222,7 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := encode(p, c.clientID, c.domain) if err != nil { - errors.LogDebug(context.Background(), addr, " mask write err ", err, " ", len(p)) + errors.LogDebug(context.Background(), addr, " xdns wireformat err ", err, " ", len(p)) return 0, nil } diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 8d5bc5a872e3..1dbc54c90139 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -165,6 +165,7 @@ func (c *xdnsConnServer) recvLoop() { query, err := MessageFromWireFormat(buf[:n]) if err != nil { + errors.LogDebug(context.Background(), addr, " xdns from wireformat err ", err) continue } @@ -189,7 +190,7 @@ func (c *xdnsConnServer) recvLoop() { addr: clientIDToAddr(clientID), }: default: - errors.LogDebug(context.Background(), "mask read err queue full") + errors.LogDebug(context.Background(), addr, " ", clientID, " mask read err queue full") } } } else { @@ -202,11 +203,13 @@ func (c *xdnsConnServer) recvLoop() { select { case c.ch <- &record{resp, addr, clientIDToAddr(clientID)}: default: - errors.LogDebug(context.Background(), "mask read err record queue full") + errors.LogDebug(context.Background(), addr, " ", clientID, " mask read err record queue full") } } } + errors.LogDebug(context.Background(), "xdns closed") + close(c.ch) close(c.readQueue) @@ -249,6 +252,7 @@ func (c *xdnsConnServer) sendLoop() { var payload bytes.Buffer limit := maxEncodedPayload timer := time.NewTimer(maxResponseDelay) + for { c.mutex.Lock() q := c.ensureQueue(rec.ClientAddr) @@ -283,34 +287,31 @@ func (c *xdnsConnServer) sendLoop() { } limit -= 2 + len(p) - if payload.Len() == 0 { - - } else if limit < 0 { + if payload.Len() > 0 && limit < 0 { c.stash(q, p) - break } - if int(uint16(len(p))) != len(p) { - panic(len(p)) - } + // if len(p) > 65535 { + // 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 { + errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns wireformat err ", err) continue } if len(buf) > maxUDPPayload { - errors.LogDebug(context.Background(), "xdns server truncate ", len(buf)) + errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns truncate ", len(buf)) buf = buf[:maxUDPPayload] buf[2] |= 0x02 } @@ -319,7 +320,11 @@ func (c *xdnsConnServer) sendLoop() { return } - _, _ = c.PacketConn.WriteTo(buf, rec.Addr) + _, err = c.PacketConn.WriteTo(buf, rec.Addr) + if go_errors.Is(err, net.ErrClosed) || go_errors.Is(err, io.ErrClosedPipe) { + c.closed = true + break + } } } @@ -329,7 +334,7 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p)) return 0, packet.addr, nil } copy(p, packet.p) @@ -340,6 +345,10 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.mutex.Lock() defer c.mutex.Unlock() + if len(p)+2 > maxEncodedPayload { + errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+2 > ", maxEncodedPayload) + } + q := c.ensureQueue(addr) if q == nil { return 0, io.ErrClosedPipe @@ -352,7 +361,7 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { case q.queue <- buf: return len(p), nil default: - errors.LogDebug(context.Background(), addr, " mask write err queue full") + // errors.LogDebug(context.Background(), addr, " mask write err queue full") return 0, nil } } diff --git a/transport/internet/finalmask/xicmp/client.go b/transport/internet/finalmask/xicmp/client.go index 04dedc8397df..0c900dc3f05a 100644 --- a/transport/internet/finalmask/xicmp/client.go +++ b/transport/internet/finalmask/xicmp/client.go @@ -206,7 +206,7 @@ func (c *xicmpConnClient) recvLoop() { addr: &net.UDPAddr{IP: addr.(*net.IPAddr).IP}, }: default: - errors.LogDebug(context.Background(), "mask read err queue full") + errors.LogDebug(context.Background(), addr, " ", echo.Seq, " ", echo.ID, " mask read err queue full") } select { @@ -216,6 +216,8 @@ func (c *xicmpConnClient) recvLoop() { } } + errors.LogDebug(context.Background(), "xicmp closed") + close(c.pollChan) close(c.readQueue) @@ -281,7 +283,7 @@ func (c *xicmpConnClient) sendLoop() { if p != nil { _, err := c.icmpConn.WriteTo(p.p, p.addr) if err != nil { - errors.LogDebug(context.Background(), "xicmp writeto err ", err) + errors.LogDebug(context.Background(), p.addr, " xicmp writeto err ", err) } } } @@ -293,7 +295,7 @@ func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p)) return 0, packet.addr, nil } copy(p, packet.p) @@ -303,7 +305,7 @@ func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) { encoded, err := c.encode(p) if err != nil { - errors.LogDebug(context.Background(), addr, " mask write err ", err) + errors.LogDebug(context.Background(), addr, " xicmp wireformat err ", err) return 0, nil } diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 5eae9989a5b0..1eb744f806a3 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -223,7 +223,7 @@ func (c *xicmpConnServer) recvLoop() { }, }: default: - errors.LogDebug(context.Background(), "mask read err queue full") + errors.LogDebug(context.Background(), addr, " ", echo.ID, " ", echo.Seq, " mask read err queue full") } } @@ -239,10 +239,12 @@ func (c *xicmpConnServer) recvLoop() { }, }: default: - errors.LogDebug(context.Background(), "mask read err record queue full") + errors.LogDebug(context.Background(), addr, " ", echo.ID, " ", echo.Seq, " mask read err record queue full") } } + errors.LogDebug(context.Background(), "xicmp closed") + close(c.ch) close(c.readQueue) @@ -300,6 +302,7 @@ func (c *xicmpConnServer) sendLoop() { buf, err := c.encode(p, rec.id, rec.seq, rec.needSeqByte, rec.seqByte) if err != nil { + errors.LogDebug(context.Background(), rec.addr, " ", rec.id, " ", rec.seq, " xicmp wireformat err ", err) continue } @@ -309,7 +312,7 @@ func (c *xicmpConnServer) sendLoop() { _, err = c.icmpConn.WriteTo(buf, &net.IPAddr{IP: rec.addr.(*net.UDPAddr).IP}) if err != nil { - errors.LogDebug(context.Background(), "xicmp writeto err ", err) + errors.LogDebug(context.Background(), rec.addr, " ", rec.id, " ", rec.seq, " xicmp writeto err ", err) } } } @@ -320,7 +323,7 @@ func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { return 0, nil, net.ErrClosed } if len(p) < len(packet.p) { - errors.LogDebug(context.Background(), addr, " mask read err short buffer ", len(p), " ", len(packet.p)) + errors.LogDebug(context.Background(), packet.addr, " mask read err short buffer ", len(p), " ", len(packet.p)) return 0, packet.addr, nil } copy(p, packet.p) @@ -331,6 +334,10 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { c.mutex.Lock() defer c.mutex.Unlock() + if len(p)+8+1 > finalmask.UDPSize { + errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+8+1 > ", finalmask.UDPSize) + } + q := c.ensureQueue(addr) if q == nil { return 0, io.ErrClosedPipe @@ -343,7 +350,7 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { case q.queue <- buf: return len(p), nil default: - errors.LogDebug(context.Background(), addr, " mask write err queue full") + // errors.LogDebug(context.Background(), addr, " mask write err queue full") return 0, nil } } From beb80ae3dfb2f45b2138a2ceb736d6c1fe4d7b31 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 4 Mar 2026 16:28:27 +0800 Subject: [PATCH 73/75] RawConn --- transport/internet/finalmask/header/custom/tcp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/internet/finalmask/header/custom/tcp.go b/transport/internet/finalmask/header/custom/tcp.go index b30c6a16a462..9f3e893a0eb6 100644 --- a/transport/internet/finalmask/header/custom/tcp.go +++ b/transport/internet/finalmask/header/custom/tcp.go @@ -44,7 +44,7 @@ func NewConnClientTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { func (c *tcpCustomClientConn) TcpMaskConn() {} func (c *tcpCustomClientConn) RawConn() net.Conn { - c.wg.Wait() + // c.wg.Wait() return c.Conn } @@ -136,7 +136,7 @@ func NewConnServerTCP(c *TCPConfig, raw net.Conn) (net.Conn, error) { func (c *tcpCustomServerConn) TcpMaskConn() {} func (c *tcpCustomServerConn) RawConn() net.Conn { - c.wg.Wait() + // c.wg.Wait() return c.Conn } From 049481315dd0825da89214160ff8d6450a4ca3a5 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 4 Mar 2026 16:40:06 +0800 Subject: [PATCH 74/75] missing return --- transport/internet/finalmask/xdns/server.go | 1 + transport/internet/finalmask/xicmp/server.go | 1 + 2 files changed, 2 insertions(+) diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index 1dbc54c90139..e6e448298955 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -347,6 +347,7 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(p)+2 > maxEncodedPayload { errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+2 > ", maxEncodedPayload) + return 0, nil } q := c.ensureQueue(addr) diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 1eb744f806a3..3f8ab1e64d36 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -336,6 +336,7 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { if len(p)+8+1 > finalmask.UDPSize { errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+8+1 > ", finalmask.UDPSize) + return 0, nil } q := c.ensureQueue(addr) From fcca3f46d656180b32dfe04e5658e4c6269e5b61 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 4 Mar 2026 16:50:17 +0800 Subject: [PATCH 75/75] 1 --- transport/internet/finalmask/xdns/server.go | 8 ++++---- transport/internet/finalmask/xicmp/server.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/transport/internet/finalmask/xdns/server.go b/transport/internet/finalmask/xdns/server.go index e6e448298955..00afe57200fc 100644 --- a/transport/internet/finalmask/xdns/server.go +++ b/transport/internet/finalmask/xdns/server.go @@ -342,14 +342,14 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { - c.mutex.Lock() - defer c.mutex.Unlock() - if len(p)+2 > maxEncodedPayload { - errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+2 > ", maxEncodedPayload) + errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+2 > ", maxEncodedPayload) return 0, nil } + c.mutex.Lock() + defer c.mutex.Unlock() + q := c.ensureQueue(addr) if q == nil { return 0, io.ErrClosedPipe diff --git a/transport/internet/finalmask/xicmp/server.go b/transport/internet/finalmask/xicmp/server.go index 3f8ab1e64d36..78d3397eb58b 100644 --- a/transport/internet/finalmask/xicmp/server.go +++ b/transport/internet/finalmask/xicmp/server.go @@ -331,14 +331,14 @@ func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) { - c.mutex.Lock() - defer c.mutex.Unlock() - if len(p)+8+1 > finalmask.UDPSize { - errors.LogDebug(context.Background(), addr, " mask write err ", len(p), "+8+1 > ", finalmask.UDPSize) + errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+8+1 > ", finalmask.UDPSize) return 0, nil } + c.mutex.Lock() + defer c.mutex.Unlock() + q := c.ensureQueue(addr) if q == nil { return 0, io.ErrClosedPipe