Finalmask: Add header-custom (TCP & UDP), fragment (TCP), noise (UDP); Support dialer-proxy, XHTTP/3; Fix XDNS, XICMP potential panic (#5657)

https://github.com/XTLS/Xray-core/pull/5657#issuecomment-4016609446
This commit is contained in:
LjhAUMEM
2026-03-07 23:42:18 +08:00
committed by GitHub
parent ea87941b77
commit a204873d79
62 changed files with 3045 additions and 1367 deletions
@@ -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, level int, levelCount int) (net.PacketConn, error) {
return NewConnClientUDP(c, raw)
}
func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
return NewConnServerUDP(c, raw)
}
@@ -0,0 +1,380 @@
// 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 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"`
Sequence []*TCPItem `protobuf:"bytes,1,rep,name=sequence,proto3" json:"sequence,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) GetSequence() []*TCPItem {
if x != nil {
return x.Sequence
}
return nil
}
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"`
Errors []*TCPSequence `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TCPConfig) Reset() {
*x = TCPConfig{}
mi := &file_transport_internet_finalmask_header_custom_config_proto_msgTypes[2]
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[2]
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{2}
}
func (x *TCPConfig) GetClients() []*TCPSequence {
if x != nil {
return x.Clients
}
return nil
}
func (x *TCPConfig) GetServers() []*TCPSequence {
if x != nil {
return x.Servers
}
return nil
}
func (x *TCPConfig) GetErrors() []*TCPSequence {
if x != nil {
return x.Errors
}
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 []*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[4]
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[4]
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{4}
}
func (x *UDPConfig) GetClient() []*UDPItem {
if x != nil {
return x.Client
}
return nil
}
func (x *UDPConfig) GetServer() []*UDPItem {
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\"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\"c\n" +
"\vTCPSequence\x12T\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\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" +
"\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 (
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, 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
(*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
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() }
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: 5,
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
}
@@ -0,0 +1,34 @@
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 TCPItem {
int64 delay_min = 1;
int64 delay_max = 2;
int32 rand = 3;
bytes packet = 4;
}
message TCPSequence {
repeated TCPItem sequence = 1;
}
message TCPConfig {
repeated TCPSequence clients = 1;
repeated TCPSequence servers = 2;
repeated TCPSequence errors = 3;
}
message UDPItem {
int32 rand = 1;
bytes packet = 2;
}
message UDPConfig {
repeated UDPItem client = 1;
repeated UDPItem server = 2;
}
@@ -0,0 +1,248 @@
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 []*TCPSequence
servers []*TCPSequence
}
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,
},
}
conn.wg.Add(1)
return conn, nil
}
func (c *tcpCustomClientConn) TcpMaskConn() {}
func (c *tcpCustomClientConn) RawConn() net.Conn {
// c.wg.Wait()
return c.Conn
}
func (c *tcpCustomClientConn) Splice() bool {
return true
}
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() {
i := 0
j := 0
for i = range c.header.clients {
if !writeSequence(c.Conn, c.header.clients[i]) {
c.wg.Done()
return
}
if j < len(c.header.servers) {
if !readSequence(c.Conn, c.header.servers[j]) {
c.wg.Done()
return
}
j++
}
}
for j < len(c.header.servers) {
if !readSequence(c.Conn, c.header.servers[j]) {
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 []*TCPSequence
servers []*TCPSequence
errors []*TCPSequence
}
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,
errors: c.Errors,
},
}
conn.wg.Add(1)
return conn, nil
}
func (c *tcpCustomServerConn) TcpMaskConn() {}
func (c *tcpCustomServerConn) RawConn() net.Conn {
// c.wg.Wait()
return c.Conn
}
func (c *tcpCustomServerConn) Splice() bool {
return true
}
func (c *tcpCustomServerConn) Read(p []byte) (n int, err error) {
c.once.Do(func() {
i := 0
j := 0
for i = range c.header.clients {
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) {
if !writeSequence(c.Conn, c.header.servers[j]) {
c.wg.Done()
return
}
j++
}
}
for j < len(c.header.servers) {
if !writeSequence(c.Conn, c.header.servers[j]) {
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 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 {
buf := make([]byte, item.Rand)
common.Must2(rand.Read(buf))
merged = append(merged, buf...)
} else {
merged = append(merged, item.Packet...)
}
}
if len(merged) > 0 {
_, err := w.Write(merged)
if err != nil {
return false
}
merged = nil
}
return true
}
@@ -0,0 +1,250 @@
package custom
import (
"bytes"
"context"
"crypto/rand"
"net"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/transport/internet/finalmask"
)
type udpCustomClient struct {
client []*UDPItem
server []*UDPItem
merged []byte
}
func (h *udpCustomClient) Serialize(b []byte) {
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)
}
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
}
func NewConnClientUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
conn := &udpCustomClientConn{
PacketConn: raw,
header: &udpCustomClient{
client: c.Client,
server: c.Server,
},
}
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) 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(buf)
if err != nil || n == 0 {
return n, addr, err
}
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, nil
}
copy(p, buf[len(c.header.merged):n])
return n - len(c.header.merged), addr, nil
}
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
}
var buf []byte
if cap(p) != finalmask.UDPSize {
buf = make([]byte, finalmask.UDPSize)
} else {
buf = p[:len(c.header.merged)+len(p)]
}
copy(buf[len(c.header.merged):], p)
c.header.Serialize(buf)
_, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr)
if err != nil {
return 0, err
}
return len(p), nil
}
type udpCustomServer struct {
client []*UDPItem
server []*UDPItem
merged []byte
}
func (h *udpCustomServer) Serialize(b []byte) {
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)
}
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
}
func NewConnServerUDP(c *UDPConfig, raw net.PacketConn) (net.PacketConn, error) {
conn := &udpCustomServerConn{
PacketConn: raw,
header: &udpCustomServer{
client: c.Client,
server: c.Server,
},
}
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) 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(buf)
if err != nil || n == 0 {
return n, addr, err
}
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, nil
}
copy(p, buf[len(c.header.merged):n])
return n - len(c.header.merged), addr, nil
}
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
}
var buf []byte
if cap(p) != finalmask.UDPSize {
buf = make([]byte, finalmask.UDPSize)
} else {
buf = p[:len(c.header.merged)+len(p)]
}
copy(buf[len(c.header.merged):], p)
c.header.Serialize(buf)
_, err = c.PacketConn.WriteTo(buf[:len(c.header.merged)+len(p)], addr)
if err != nil {
return 0, err
}
return len(p), nil
}