2020-11-25 19:01:53 +08:00
package conf
import (
2026-05-02 09:54:34 +08:00
"context"
2024-09-16 05:46:30 -07:00
"encoding/base64"
2025-01-02 12:47:03 +03:30
"encoding/hex"
2020-11-25 19:01:53 +08:00
"net"
"strings"
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2026-04-16 07:41:11 +08:00
"github.com/xtls/xray-core/common/geodata"
2026-05-02 09:54:34 +08:00
xnet "github.com/xtls/xray-core/common/net"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/proxy/freedom"
2025-08-19 16:03:12 +02:00
"github.com/xtls/xray-core/transport/internet"
2026-03-07 23:42:18 +08:00
"google.golang.org/protobuf/proto"
2020-11-25 19:01:53 +08:00
)
type FreedomConfig struct {
2026-05-02 09:54:34 +08:00
TargetStrategy string ` json:"targetStrategy" `
DomainStrategy string ` json:"domainStrategy" `
Redirect string ` json:"redirect" `
UserLevel uint32 ` json:"userLevel" `
Fragment * Fragment ` json:"fragment" `
Noise * Noise ` json:"noise" `
Noises [ ] * Noise ` json:"noises" `
ProxyProtocol uint32 ` json:"proxyProtocol" `
IPsBlocked * StringList ` json:"ipsBlocked" `
FinalRules [ ] * FreedomFinalRuleConfig ` json:"finalRules" `
2023-05-22 04:59:58 +02:00
}
type Fragment struct {
2024-10-15 12:05:22 +08:00
Packets string ` json:"packets" `
Length * Int32Range ` json:"length" `
Interval * Int32Range ` json:"interval" `
2025-08-08 01:56:09 +02:00
MaxSplit * Int32Range ` json:"maxSplit" `
2020-11-25 19:01:53 +08:00
}
2024-08-28 14:10:11 -07:00
type Noise struct {
2025-08-08 01:56:09 +02:00
Type string ` json:"type" `
Packet string ` json:"packet" `
Delay * Int32Range ` json:"delay" `
ApplyTo string ` json:"applyTo" `
2024-08-28 14:10:11 -07:00
}
2026-05-02 09:54:34 +08:00
type FreedomFinalRuleConfig struct {
2026-05-03 04:40:46 +08:00
Action string ` json:"action" `
Network * NetworkList ` json:"network" `
Port * PortList ` json:"port" `
IP * StringList ` json:"ip" `
BlockDelay * Int32Range ` json:"blockDelay" `
2026-05-02 09:54:34 +08:00
}
2020-11-25 19:01:53 +08:00
// Build implements Buildable
func ( c * FreedomConfig ) Build ( ) ( proto . Message , error ) {
2026-05-02 09:54:34 +08:00
if c . IPsBlocked != nil {
// todo: remove legacy
errors . LogWarning ( context . Background ( ) , ` The feature "ipsBlocked" has been removed and migrated to "finalRules". Please update your config(s) according to release note and documentation. ` )
}
2020-11-25 19:01:53 +08:00
config := new ( freedom . Config )
2025-08-16 16:48:53 +02:00
targetStrategy := c . TargetStrategy
if targetStrategy == "" {
targetStrategy = c . DomainStrategy
}
switch strings . ToLower ( targetStrategy ) {
2023-11-12 16:27:39 -05:00
case "asis" , "" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_AS_IS
2023-11-12 16:27:39 -05:00
case "useip" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_USE_IP
2023-11-12 16:27:39 -05:00
case "useipv4" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_USE_IP4
2023-11-12 16:27:39 -05:00
case "useipv6" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_USE_IP6
2023-11-12 16:27:39 -05:00
case "useipv4v6" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_USE_IP46
2023-11-12 16:27:39 -05:00
case "useipv6v4" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_USE_IP64
2023-11-12 16:27:39 -05:00
case "forceip" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_FORCE_IP
2023-11-12 16:27:39 -05:00
case "forceipv4" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_FORCE_IP4
2023-11-12 16:27:39 -05:00
case "forceipv6" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_FORCE_IP6
2023-11-12 16:27:39 -05:00
case "forceipv4v6" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_FORCE_IP46
2023-11-12 16:27:39 -05:00
case "forceipv6v4" :
2025-08-19 16:03:12 +02:00
config . DomainStrategy = internet . DomainStrategy_FORCE_IP64
2023-11-12 16:27:39 -05:00
default :
2025-08-16 16:48:53 +02:00
return nil , errors . New ( "unsupported domain strategy: " , targetStrategy )
2020-11-25 19:01:53 +08:00
}
2023-05-22 04:59:58 +02:00
if c . Fragment != nil {
2023-07-06 16:30:39 +00:00
config . Fragment = new ( freedom . Fragment )
2023-05-22 04:59:58 +02:00
2023-06-18 07:12:42 -07:00
switch strings . ToLower ( c . Fragment . Packets ) {
case "tlshello" :
// TLS Hello Fragmentation (into multiple handshake messages)
2023-07-06 16:30:39 +00:00
config . Fragment . PacketsFrom = 0
config . Fragment . PacketsTo = 1
2023-06-18 07:12:42 -07:00
case "" :
// TCP Segmentation (all packets)
2023-07-06 16:30:39 +00:00
config . Fragment . PacketsFrom = 0
config . Fragment . PacketsTo = 0
2023-06-18 07:12:42 -07:00
default :
// TCP Segmentation (range)
2024-10-15 12:05:22 +08:00
from , to , err := ParseRangeString ( c . Fragment . Packets )
2023-05-22 04:59:58 +02:00
if err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Invalid PacketsFrom" ) . Base ( err )
2023-05-22 04:59:58 +02:00
}
2024-10-15 12:05:22 +08:00
config . Fragment . PacketsFrom = uint64 ( from )
config . Fragment . PacketsTo = uint64 ( to )
2023-07-06 16:30:39 +00:00
if config . Fragment . PacketsFrom == 0 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "PacketsFrom can't be 0" )
2023-07-06 16:30:39 +00:00
}
}
{
2024-10-15 12:05:22 +08:00
if c . Fragment . Length == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Length can't be empty" )
2023-07-06 16:30:39 +00:00
}
2024-10-15 12:05:22 +08:00
config . Fragment . LengthMin = uint64 ( c . Fragment . Length . From )
config . Fragment . LengthMax = uint64 ( c . Fragment . Length . To )
2023-07-06 16:30:39 +00:00
if config . Fragment . LengthMin == 0 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "LengthMin can't be 0" )
2023-07-06 16:30:39 +00:00
}
}
{
2024-10-15 12:05:22 +08:00
if c . Fragment . Interval == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Interval can't be empty" )
2023-07-06 16:30:39 +00:00
}
2024-10-15 12:05:22 +08:00
config . Fragment . IntervalMin = uint64 ( c . Fragment . Interval . From )
config . Fragment . IntervalMax = uint64 ( c . Fragment . Interval . To )
2023-05-22 04:59:58 +02:00
}
2025-08-08 01:56:09 +02:00
{
if c . Fragment . MaxSplit != nil {
config . Fragment . MaxSplitMin = uint64 ( c . Fragment . MaxSplit . From )
config . Fragment . MaxSplitMax = uint64 ( c . Fragment . MaxSplit . To )
}
}
2023-05-22 04:59:58 +02:00
}
2024-08-28 14:10:11 -07:00
2024-09-16 05:46:30 -07:00
if c . Noise != nil {
return nil , errors . PrintRemovedFeatureError ( "noise = { ... }" , "noises = [ { ... } ]" )
}
2024-08-28 14:10:11 -07:00
2024-09-16 05:46:30 -07:00
if c . Noises != nil {
for _ , n := range c . Noises {
NConfig , err := ParseNoise ( n )
2024-08-28 14:10:11 -07:00
if err != nil {
2024-09-16 05:46:30 -07:00
return nil , err
2024-08-28 14:10:11 -07:00
}
2024-09-16 05:46:30 -07:00
config . Noises = append ( config . Noises , NConfig )
2024-08-28 14:10:11 -07:00
}
}
2023-05-22 04:59:58 +02:00
2020-11-25 19:01:53 +08:00
config . UserLevel = c . UserLevel
2026-05-02 09:54:34 +08:00
2020-11-25 19:01:53 +08:00
if len ( c . Redirect ) > 0 {
host , portStr , err := net . SplitHostPort ( c . Redirect )
if err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "invalid redirect address: " , c . Redirect , ": " , err ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
2026-05-02 09:54:34 +08:00
port , err := xnet . PortFromString ( portStr )
2020-11-25 19:01:53 +08:00
if err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "invalid redirect port: " , c . Redirect , ": " , err ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
config . DestinationOverride = & freedom . DestinationOverride {
Server : & protocol . ServerEndpoint {
Port : uint32 ( port ) ,
} ,
}
if len ( host ) > 0 {
2026-05-02 09:54:34 +08:00
config . DestinationOverride . Server . Address = xnet . NewIPOrDomain ( xnet . ParseAddress ( host ) )
2020-11-25 19:01:53 +08:00
}
}
2026-05-02 09:54:34 +08:00
2024-02-03 09:10:23 +08:00
if c . ProxyProtocol > 0 && c . ProxyProtocol <= 2 {
config . ProxyProtocol = c . ProxyProtocol
}
2026-05-02 09:54:34 +08:00
for _ , r := range c . FinalRules {
rule , err := r . Build ( )
2026-04-16 07:41:11 +08:00
if err != nil {
return nil , err
}
2026-05-02 09:54:34 +08:00
config . FinalRules = append ( config . FinalRules , rule )
2026-04-16 07:41:11 +08:00
}
2026-05-02 09:54:34 +08:00
2020-11-25 19:01:53 +08:00
return config , nil
}
2024-09-16 05:46:30 -07:00
func ParseNoise ( noise * Noise ) ( * freedom . Noise , error ) {
2024-10-15 12:05:22 +08:00
var err error
2024-09-16 05:46:30 -07:00
NConfig := new ( freedom . Noise )
2025-01-02 09:45:46 +00:00
noise . Packet = strings . TrimSpace ( noise . Packet )
2024-09-16 05:46:30 -07:00
2025-01-02 09:45:46 +00:00
switch noise . Type {
2024-09-16 05:46:30 -07:00
case "rand" :
2024-10-15 12:05:22 +08:00
min , max , err := ParseRangeString ( noise . Packet )
2024-09-16 05:46:30 -07:00
if err != nil {
2024-10-15 12:05:22 +08:00
return nil , errors . New ( "invalid value for rand Length" ) . Base ( err )
2024-09-16 05:46:30 -07:00
}
2024-10-15 12:05:22 +08:00
NConfig . LengthMin = uint64 ( min )
NConfig . LengthMax = uint64 ( max )
2024-09-16 05:46:30 -07:00
if NConfig . LengthMin == 0 {
return nil , errors . New ( "rand lengthMin or lengthMax cannot be 0" )
}
case "str" :
2025-01-02 12:47:03 +03:30
// user input string
2025-01-02 09:45:46 +00:00
NConfig . Packet = [ ] byte ( noise . Packet )
2024-09-16 05:46:30 -07:00
2025-01-02 12:47:03 +03:30
case "hex" :
// user input hex
NConfig . Packet , err = hex . DecodeString ( noise . Packet )
if err != nil {
return nil , errors . New ( "Invalid hex string" ) . Base ( err )
}
2024-09-16 05:46:30 -07:00
case "base64" :
2025-01-02 12:47:03 +03:30
// user input base64
2025-01-02 09:45:46 +00:00
NConfig . Packet , err = base64 . RawURLEncoding . DecodeString ( strings . NewReplacer ( "+" , "-" , "/" , "_" , "=" , "" ) . Replace ( noise . Packet ) )
2024-09-16 05:46:30 -07:00
if err != nil {
2025-01-02 12:47:03 +03:30
return nil , errors . New ( "Invalid base64 string" ) . Base ( err )
2024-09-16 05:46:30 -07:00
}
default :
2025-01-02 12:47:03 +03:30
return nil , errors . New ( "Invalid packet, only rand/str/hex/base64 are supported" )
2024-09-16 05:46:30 -07:00
}
if noise . Delay != nil {
2025-01-01 13:52:57 +03:30
NConfig . DelayMin = uint64 ( noise . Delay . From )
NConfig . DelayMax = uint64 ( noise . Delay . To )
2024-09-16 05:46:30 -07:00
}
2025-08-08 01:56:09 +02:00
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" )
}
2024-09-16 05:46:30 -07:00
return NConfig , nil
}
2026-05-02 09:54:34 +08:00
func ( c * FreedomFinalRuleConfig ) Build ( ) ( * freedom . FinalRuleConfig , error ) {
rule := & freedom . FinalRuleConfig { }
switch strings . ToLower ( c . Action ) {
case "allow" :
rule . Action = freedom . RuleAction_Allow
case "block" :
rule . Action = freedom . RuleAction_Block
default :
return nil , errors . New ( "unknown action: " , c . Action )
}
if c . Network != nil {
rule . Networks = c . Network . Build ( )
}
if c . Port != nil {
rule . PortList = c . Port . Build ( )
}
if c . IP != nil {
rules , err := geodata . ParseIPRules ( * c . IP )
if err != nil {
return nil , err
}
rule . Ip = rules
}
2026-05-03 04:40:46 +08:00
if c . BlockDelay != nil {
rule . BlockDelay = & freedom . Range {
Min : uint64 ( c . BlockDelay . From ) ,
Max : uint64 ( c . BlockDelay . To ) ,
}
}
2026-05-02 09:54:34 +08:00
return rule , nil
}