From 363e86c5857eb963b23183a19024d1d28ebbd03f Mon Sep 17 00:00:00 2001 From: dragonbreath2000 <168475359+dragonbreath2000@users.noreply.github.com> Date: Mon, 16 Sep 2024 05:46:30 -0700 Subject: [PATCH] UDP noises: Add base64 and multi-packet support (#3794) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/XTLS/Xray-core/pull/3794#issuecomment-2351329251 --------- Co-authored-by: 风扇滑翔翼 Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com> --- infra/conf/freedom.go | 145 ++++++++++++++++++++----------------- proxy/freedom/config.pb.go | 60 +++++++-------- proxy/freedom/config.proto | 4 +- proxy/freedom/freedom.go | 42 +++++------ 4 files changed, 131 insertions(+), 120 deletions(-) diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index e172afa8..4a54936f 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -1,6 +1,7 @@ package conf import ( + "encoding/base64" "net" "strconv" "strings" @@ -19,6 +20,7 @@ type FreedomConfig struct { UserLevel uint32 `json:"userLevel"` Fragment *Fragment `json:"fragment"` Noise *Noise `json:"noise"` + Noises []*Noise `json:"noises"` ProxyProtocol uint32 `json:"proxyProtocol"` } @@ -29,8 +31,9 @@ type Fragment struct { } type Noise struct { - Packet string `json:"packet"` - Delay string `json:"delay"` + Type string `json:"type"` + Packet string `json:"packet"` + Delay *Int32Range `json:"delay"` } // Build implements Buildable @@ -149,74 +152,18 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } } } + if c.Noise != nil { - config.Noise = new(freedom.Noise) - var err, err2 error - p := strings.Split(strings.ToLower(c.Noise.Packet), ":") - if len(p) != 2 { - return nil, errors.New("invalid type for packet") - } - switch p[0] { - case "rand": - randValue := strings.Split(p[1], "-") - if len(randValue) > 2 { - return nil, errors.New("Only 2 values are allowed for rand") - } - if len(randValue) == 2 { - config.Noise.LengthMin, err = strconv.ParseUint(randValue[0], 10, 64) - config.Noise.LengthMax, err2 = strconv.ParseUint(randValue[1], 10, 64) - } - if len(randValue) == 1 { - config.Noise.LengthMin, err = strconv.ParseUint(randValue[0], 10, 64) - config.Noise.LengthMax = config.Noise.LengthMin - } + return nil, errors.PrintRemovedFeatureError("noise = { ... }", "noises = [ { ... } ]") + } + + if c.Noises != nil { + for _, n := range c.Noises { + NConfig, err := ParseNoise(n) if err != nil { - return nil, errors.New("invalid value for rand LengthMin").Base(err) + return nil, err } - if err2 != nil { - return nil, errors.New("invalid value for rand LengthMax").Base(err2) - } - if config.Noise.LengthMin > config.Noise.LengthMax { - config.Noise.LengthMin, config.Noise.LengthMax = config.Noise.LengthMax, config.Noise.LengthMin - } - if config.Noise.LengthMin == 0 { - return nil, errors.New("rand lengthMin or lengthMax cannot be 0") - } - - case "str": - //user input string - config.Noise.StrNoise = strings.TrimSpace(p[1]) - - default: - return nil, errors.New("Invalid packet,only rand and str are supported") - } - if c.Noise.Delay != "" { - d := strings.Split(strings.ToLower(c.Noise.Delay), "-") - if len(d) > 2 { - return nil, errors.New("Invalid delay value") - } - if len(d) == 2 { - config.Noise.DelayMin, err = strconv.ParseUint(d[0], 10, 64) - config.Noise.DelayMax, err2 = strconv.ParseUint(d[1], 10, 64) - - } else { - config.Noise.DelayMin, err = strconv.ParseUint(d[0], 10, 64) - config.Noise.DelayMax = config.Noise.DelayMin - } - if err != nil { - return nil, errors.New("Invalid value for DelayMin").Base(err) - } - if err2 != nil { - return nil, errors.New("Invalid value for DelayMax").Base(err2) - } - if config.Noise.DelayMin > config.Noise.DelayMax { - config.Noise.DelayMin, config.Noise.DelayMax = config.Noise.DelayMax, config.Noise.DelayMin - } - if config.Noise.DelayMin == 0 { - return nil, errors.New("DelayMin or DelayMax cannot be 0") - } - } else { - config.Noise.DelayMin = 0 + config.Noises = append(config.Noises, NConfig) } } @@ -248,3 +195,67 @@ func (c *FreedomConfig) Build() (proto.Message, error) { } return config, nil } + +func ParseNoise(noise *Noise) (*freedom.Noise, error) { + var err, err2 error + NConfig := new(freedom.Noise) + + switch strings.ToLower(noise.Type) { + case "rand": + randValue := strings.Split(noise.Packet, "-") + if len(randValue) > 2 { + return nil, errors.New("Only 2 values are allowed for rand") + } + if len(randValue) == 2 { + NConfig.LengthMin, err = strconv.ParseUint(randValue[0], 10, 64) + NConfig.LengthMax, err2 = strconv.ParseUint(randValue[1], 10, 64) + } + if len(randValue) == 1 { + NConfig.LengthMin, err = strconv.ParseUint(randValue[0], 10, 64) + NConfig.LengthMax = NConfig.LengthMin + } + if err != nil { + return nil, errors.New("invalid value for rand LengthMin").Base(err) + } + if err2 != nil { + return nil, errors.New("invalid value for rand LengthMax").Base(err2) + } + if NConfig.LengthMin > NConfig.LengthMax { + NConfig.LengthMin, NConfig.LengthMax = NConfig.LengthMax, NConfig.LengthMin + } + if NConfig.LengthMin == 0 { + return nil, errors.New("rand lengthMin or lengthMax cannot be 0") + } + + case "str": + //user input string + NConfig.StrNoise = []byte(strings.TrimSpace(noise.Packet)) + + case "base64": + //user input base64 + NConfig.StrNoise, err = base64.StdEncoding.DecodeString(strings.TrimSpace(noise.Packet)) + if err != nil { + return nil, errors.New("Invalid base64 string") + } + + default: + return nil, errors.New("Invalid packet,only rand,str,base64 are supported") + } + + if noise.Delay != nil { + if noise.Delay.From != 0 && noise.Delay.To != 0 { + NConfig.DelayMin = uint64(noise.Delay.From) + NConfig.DelayMax = uint64(noise.Delay.To) + if NConfig.DelayMin > NConfig.LengthMax { + NConfig.DelayMin, NConfig.DelayMax = NConfig.LengthMax, NConfig.DelayMin + } + } else { + return nil, errors.New("DelayMin or DelayMax cannot be zero") + } + + } else { + NConfig.DelayMin = 0 + NConfig.DelayMax = 0 + } + return NConfig, nil +} diff --git a/proxy/freedom/config.pb.go b/proxy/freedom/config.pb.go index 9d829251..f7b307c4 100644 --- a/proxy/freedom/config.pb.go +++ b/proxy/freedom/config.pb.go @@ -237,7 +237,7 @@ type Noise struct { LengthMax uint64 `protobuf:"varint,2,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"` DelayMin uint64 `protobuf:"varint,3,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` DelayMax uint64 `protobuf:"varint,4,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` - StrNoise string `protobuf:"bytes,5,opt,name=str_noise,json=strNoise,proto3" json:"str_noise,omitempty"` + StrNoise []byte `protobuf:"bytes,5,opt,name=str_noise,json=strNoise,proto3" json:"str_noise,omitempty"` } func (x *Noise) Reset() { @@ -300,11 +300,11 @@ func (x *Noise) GetDelayMax() uint64 { return 0 } -func (x *Noise) GetStrNoise() string { +func (x *Noise) GetStrNoise() []byte { if x != nil { return x.StrNoise } - return "" + return nil } type Config struct { @@ -319,7 +319,7 @@ type Config struct { UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` Fragment *Fragment `protobuf:"bytes,5,opt,name=fragment,proto3" json:"fragment,omitempty"` ProxyProtocol uint32 `protobuf:"varint,6,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"` - Noise *Noise `protobuf:"bytes,7,opt,name=noise,proto3" json:"noise,omitempty"` + Noises []*Noise `protobuf:"bytes,7,rep,name=noises,proto3" json:"noises,omitempty"` } func (x *Config) Reset() { @@ -397,9 +397,9 @@ func (x *Config) GetProxyProtocol() uint32 { return 0 } -func (x *Config) GetNoise() *Noise { +func (x *Config) GetNoises() []*Noise { if x != nil { - return x.Noise + return x.Noises } return nil } @@ -439,8 +439,8 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a, - 0x09, 0x73, 0x74, 0x72, 0x5f, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x73, 0x74, 0x72, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x22, 0xb3, 0x04, 0x0a, 0x06, 0x43, + 0x09, 0x73, 0x74, 0x72, 0x5f, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x73, 0x74, 0x72, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x22, 0xb5, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, @@ -462,27 +462,27 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x12, 0x2f, 0x0a, 0x05, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x05, 0x6e, - 0x6f, 0x69, 0x73, 0x65, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, - 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, - 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, - 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, - 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, - 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, - 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, - 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, - 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, - 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, - 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x63, 0x6f, 0x6c, 0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x06, + 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, + 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, + 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, + 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, + 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, + 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, + 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, + 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, + 0x10, 0x0a, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, + 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, + 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -512,7 +512,7 @@ var file_proxy_freedom_config_proto_depIdxs = []int32{ 0, // 1: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.proxy.freedom.Config.DomainStrategy 1, // 2: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride 2, // 3: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment - 3, // 4: xray.proxy.freedom.Config.noise:type_name -> xray.proxy.freedom.Noise + 3, // 4: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise 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 diff --git a/proxy/freedom/config.proto b/proxy/freedom/config.proto index 85b71979..b3bef518 100644 --- a/proxy/freedom/config.proto +++ b/proxy/freedom/config.proto @@ -25,7 +25,7 @@ message Noise { uint64 length_max = 2; uint64 delay_min = 3; uint64 delay_max = 4; - string str_noise = 5; + bytes str_noise = 5; } message Config { @@ -48,5 +48,5 @@ message Config { uint32 user_level = 4; Fragment fragment = 5; uint32 proxy_protocol = 6; - Noise noise = 7; + repeated Noise noises = 7; } diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 4c3d963e..30028bc6 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -208,12 +208,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } } else { writer = NewPacketWriter(conn, h, ctx, UDPOverride) - if h.config.Noise != nil { - errors.LogDebug(ctx, "NOISE", h.config.Noise.StrNoise, h.config.Noise.LengthMin, h.config.Noise.LengthMax, - h.config.Noise.DelayMin, h.config.Noise.DelayMax) + if h.config.Noises != nil { + errors.LogDebug(ctx, "NOISE", h.config.Noises) writer = &NoisePacketWriter{ Writer: writer, - noise: h.config.Noise, + noises: h.config.Noises, firstWrite: true, UDPOverride: UDPOverride, } @@ -396,12 +395,12 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { type NoisePacketWriter struct { buf.Writer - noise *Noise + noises []*Noise firstWrite bool UDPOverride net.Destination } -// MultiBuffer writer with Noise in first packet +// MultiBuffer writer with Noise before first packet func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { if w.firstWrite { w.firstWrite = false @@ -411,22 +410,23 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { } var noise []byte var err error - //User input string - if w.noise.StrNoise != "" { - noise = []byte(w.noise.StrNoise) - } else { - //Random noise - noise, err = GenerateRandomBytes(randBetween(int64(w.noise.LengthMin), - int64(w.noise.LengthMax))) - } + for _, n := range w.noises { + //User input string or base64 encoded string + if n.StrNoise != nil { + noise = n.StrNoise + } else { + //Random noise + noise, err = GenerateRandomBytes(randBetween(int64(n.LengthMin), + int64(n.LengthMax))) + } + if err != nil { + return err + } + w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) - if err != nil { - return err - } - w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) - - if w.noise.DelayMin != 0 { - time.Sleep(time.Duration(randBetween(int64(w.noise.DelayMin), int64(w.noise.DelayMax))) * time.Millisecond) + if n.DelayMin != 0 { + time.Sleep(time.Duration(randBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond) + } } }