mirror of https://github.com/XTLS/Xray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
7.9 KiB
250 lines
7.9 KiB
package conf |
|
|
|
import ( |
|
"net" |
|
"strconv" |
|
"strings" |
|
|
|
"github.com/xtls/xray-core/common/errors" |
|
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" |
|
) |
|
|
|
type FreedomConfig struct { |
|
DomainStrategy string `json:"domainStrategy"` |
|
Timeout *uint32 `json:"timeout"` |
|
Redirect string `json:"redirect"` |
|
UserLevel uint32 `json:"userLevel"` |
|
Fragment *Fragment `json:"fragment"` |
|
Noise *Noise `json:"noise"` |
|
ProxyProtocol uint32 `json:"proxyProtocol"` |
|
} |
|
|
|
type Fragment struct { |
|
Packets string `json:"packets"` |
|
Length string `json:"length"` |
|
Interval string `json:"interval"` |
|
} |
|
|
|
type Noise struct { |
|
Packet string `json:"packet"` |
|
Delay string `json:"delay"` |
|
} |
|
|
|
// Build implements Buildable |
|
func (c *FreedomConfig) Build() (proto.Message, error) { |
|
config := new(freedom.Config) |
|
switch strings.ToLower(c.DomainStrategy) { |
|
case "asis", "": |
|
config.DomainStrategy = freedom.Config_AS_IS |
|
case "useip": |
|
config.DomainStrategy = freedom.Config_USE_IP |
|
case "useipv4": |
|
config.DomainStrategy = freedom.Config_USE_IP4 |
|
case "useipv6": |
|
config.DomainStrategy = freedom.Config_USE_IP6 |
|
case "useipv4v6": |
|
config.DomainStrategy = freedom.Config_USE_IP46 |
|
case "useipv6v4": |
|
config.DomainStrategy = freedom.Config_USE_IP64 |
|
case "forceip": |
|
config.DomainStrategy = freedom.Config_FORCE_IP |
|
case "forceipv4": |
|
config.DomainStrategy = freedom.Config_FORCE_IP4 |
|
case "forceipv6": |
|
config.DomainStrategy = freedom.Config_FORCE_IP6 |
|
case "forceipv4v6": |
|
config.DomainStrategy = freedom.Config_FORCE_IP46 |
|
case "forceipv6v4": |
|
config.DomainStrategy = freedom.Config_FORCE_IP64 |
|
default: |
|
return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) |
|
} |
|
|
|
if c.Fragment != nil { |
|
config.Fragment = new(freedom.Fragment) |
|
var err, err2 error |
|
|
|
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) |
|
packetsFromTo := strings.Split(c.Fragment.Packets, "-") |
|
if len(packetsFromTo) == 2 { |
|
config.Fragment.PacketsFrom, err = strconv.ParseUint(packetsFromTo[0], 10, 64) |
|
config.Fragment.PacketsTo, err2 = strconv.ParseUint(packetsFromTo[1], 10, 64) |
|
} else { |
|
config.Fragment.PacketsFrom, err = strconv.ParseUint(packetsFromTo[0], 10, 64) |
|
config.Fragment.PacketsTo = config.Fragment.PacketsFrom |
|
} |
|
if err != nil { |
|
return nil, errors.New("Invalid PacketsFrom").Base(err) |
|
} |
|
if err2 != nil { |
|
return nil, errors.New("Invalid PacketsTo").Base(err2) |
|
} |
|
if config.Fragment.PacketsFrom > config.Fragment.PacketsTo { |
|
config.Fragment.PacketsFrom, config.Fragment.PacketsTo = config.Fragment.PacketsTo, config.Fragment.PacketsFrom |
|
} |
|
if config.Fragment.PacketsFrom == 0 { |
|
return nil, errors.New("PacketsFrom can't be 0") |
|
} |
|
} |
|
|
|
{ |
|
if c.Fragment.Length == "" { |
|
return nil, errors.New("Length can't be empty") |
|
} |
|
lengthMinMax := strings.Split(c.Fragment.Length, "-") |
|
if len(lengthMinMax) == 2 { |
|
config.Fragment.LengthMin, err = strconv.ParseUint(lengthMinMax[0], 10, 64) |
|
config.Fragment.LengthMax, err2 = strconv.ParseUint(lengthMinMax[1], 10, 64) |
|
} else { |
|
config.Fragment.LengthMin, err = strconv.ParseUint(lengthMinMax[0], 10, 64) |
|
config.Fragment.LengthMax = config.Fragment.LengthMin |
|
} |
|
if err != nil { |
|
return nil, errors.New("Invalid LengthMin").Base(err) |
|
} |
|
if err2 != nil { |
|
return nil, errors.New("Invalid LengthMax").Base(err2) |
|
} |
|
if config.Fragment.LengthMin > config.Fragment.LengthMax { |
|
config.Fragment.LengthMin, config.Fragment.LengthMax = config.Fragment.LengthMax, config.Fragment.LengthMin |
|
} |
|
if config.Fragment.LengthMin == 0 { |
|
return nil, errors.New("LengthMin can't be 0") |
|
} |
|
} |
|
|
|
{ |
|
if c.Fragment.Interval == "" { |
|
return nil, errors.New("Interval can't be empty") |
|
} |
|
intervalMinMax := strings.Split(c.Fragment.Interval, "-") |
|
if len(intervalMinMax) == 2 { |
|
config.Fragment.IntervalMin, err = strconv.ParseUint(intervalMinMax[0], 10, 64) |
|
config.Fragment.IntervalMax, err2 = strconv.ParseUint(intervalMinMax[1], 10, 64) |
|
} else { |
|
config.Fragment.IntervalMin, err = strconv.ParseUint(intervalMinMax[0], 10, 64) |
|
config.Fragment.IntervalMax = config.Fragment.IntervalMin |
|
} |
|
if err != nil { |
|
return nil, errors.New("Invalid IntervalMin").Base(err) |
|
} |
|
if err2 != nil { |
|
return nil, errors.New("Invalid IntervalMax").Base(err2) |
|
} |
|
if config.Fragment.IntervalMin > config.Fragment.IntervalMax { |
|
config.Fragment.IntervalMin, config.Fragment.IntervalMax = config.Fragment.IntervalMax, config.Fragment.IntervalMin |
|
} |
|
} |
|
} |
|
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 |
|
} |
|
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 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 |
|
} |
|
} |
|
|
|
if c.Timeout != nil { |
|
config.Timeout = *c.Timeout |
|
} |
|
config.UserLevel = c.UserLevel |
|
if len(c.Redirect) > 0 { |
|
host, portStr, err := net.SplitHostPort(c.Redirect) |
|
if err != nil { |
|
return nil, errors.New("invalid redirect address: ", c.Redirect, ": ", err).Base(err) |
|
} |
|
port, err := v2net.PortFromString(portStr) |
|
if err != nil { |
|
return nil, errors.New("invalid redirect port: ", c.Redirect, ": ", err).Base(err) |
|
} |
|
config.DestinationOverride = &freedom.DestinationOverride{ |
|
Server: &protocol.ServerEndpoint{ |
|
Port: uint32(port), |
|
}, |
|
} |
|
|
|
if len(host) > 0 { |
|
config.DestinationOverride.Server.Address = v2net.NewIPOrDomain(v2net.ParseAddress(host)) |
|
} |
|
} |
|
if c.ProxyProtocol > 0 && c.ProxyProtocol <= 2 { |
|
config.ProxyProtocol = c.ProxyProtocol |
|
} |
|
return config, nil |
|
}
|
|
|