2020-11-25 19:01:53 +08:00
package conf
import (
2024-06-29 14:32:57 -04:00
"context"
2020-11-25 19:01:53 +08:00
"encoding/json"
2026-07-10 10:56:49 +08:00
"os"
2023-12-22 17:42:04 +08:00
"path/filepath"
2020-11-25 19:01:53 +08:00
"strings"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats"
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2026-04-14 01:39:53 +08:00
"github.com/xtls/xray-core/common/geodata"
2024-03-21 10:15:07 +00:00
"github.com/xtls/xray-core/common/net"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/serial"
core "github.com/xtls/xray-core/core"
2026-09-08 11:31:25 +08:00
"github.com/xtls/xray-core/proxy/freedom"
2026-09-24 10:13:37 +08:00
"github.com/xtls/xray-core/proxy/masque"
2021-12-14 19:28:47 -05:00
"github.com/xtls/xray-core/transport/internet"
2020-11-25 19:01:53 +08:00
)
var (
inboundConfigLoader = NewJSONConfigLoader ( ConfigCreatorCache {
2025-08-01 11:20:53 +00:00
"tunnel" : func () interface {} { return new ( DokodemoConfig ) },
2022-05-23 20:45:30 +08:00
"dokodemo-door" : func () interface {} { return new ( DokodemoConfig ) },
"http" : func () interface {} { return new ( HTTPServerConfig ) },
"shadowsocks" : func () interface {} { return new ( ShadowsocksServerConfig ) },
2024-12-31 11:55:16 +00:00
"mixed" : func () interface {} { return new ( SocksServerConfig ) },
2022-05-23 20:45:30 +08:00
"socks" : func () interface {} { return new ( SocksServerConfig ) },
"vless" : func () interface {} { return new ( VLessInboundConfig ) },
"vmess" : func () interface {} { return new ( VMessInboundConfig ) },
"trojan" : func () interface {} { return new ( TrojanServerConfig ) },
2023-11-18 11:27:17 +08:00
"wireguard" : func () interface {} { return & WireGuardConfig { IsClient : false } },
2026-02-12 22:56:06 +08:00
"hysteria" : func () interface {} { return new ( HysteriaServerConfig ) },
2026-09-27 22:46:54 +03:30
"masque" : func () interface {} { return new ( MasqueServerConfig ) },
2026-01-07 23:05:08 +01:00
"tun" : func () interface {} { return new ( TunConfig ) },
2020-11-25 19:01:53 +08:00
}, "protocol" , "settings" )
outboundConfigLoader = NewJSONConfigLoader ( ConfigCreatorCache {
2025-08-01 11:20:53 +00:00
"block" : func () interface {} { return new ( BlackholeConfig ) },
2022-05-23 20:45:30 +08:00
"blackhole" : func () interface {} { return new ( BlackholeConfig ) },
"loopback" : func () interface {} { return new ( LoopbackConfig ) },
2025-08-01 11:20:53 +00:00
"direct" : func () interface {} { return new ( FreedomConfig ) },
2022-05-23 20:45:30 +08:00
"freedom" : func () interface {} { return new ( FreedomConfig ) },
"http" : func () interface {} { return new ( HTTPClientConfig ) },
"shadowsocks" : func () interface {} { return new ( ShadowsocksClientConfig ) },
"socks" : func () interface {} { return new ( SocksClientConfig ) },
"vless" : func () interface {} { return new ( VLessOutboundConfig ) },
"vmess" : func () interface {} { return new ( VMessOutboundConfig ) },
"trojan" : func () interface {} { return new ( TrojanClientConfig ) },
2026-01-13 21:31:51 +08:00
"hysteria" : func () interface {} { return new ( HysteriaClientConfig ) },
2026-09-24 10:13:37 +08:00
"masque" : func () interface {} { return new ( MasqueClientConfig ) },
2022-05-23 20:45:30 +08:00
"dns" : func () interface {} { return new ( DNSOutboundConfig ) },
2023-11-18 11:27:17 +08:00
"wireguard" : func () interface {} { return & WireGuardConfig { IsClient : true } },
2020-11-25 19:01:53 +08:00
}, "protocol" , "settings" )
)
type SniffingConfig struct {
2026-04-14 01:39:53 +08:00
Enabled bool `json:"enabled"`
DestOverride StringList `json:"destOverride"`
DomainsExcluded StringList `json:"domainsExcluded"`
2026-04-14 02:08:51 +08:00
IPsExcluded StringList `json:"ipsExcluded"`
2026-04-14 01:39:53 +08:00
MetadataOnly bool `json:"metadataOnly"`
RouteOnly bool `json:"routeOnly"`
2020-11-25 19:01:53 +08:00
}
// Build implements Buildable.
func ( c * SniffingConfig ) Build () ( * proxyman . SniffingConfig , error ) {
2026-04-14 02:08:51 +08:00
var protocols [] string
2026-04-14 01:39:53 +08:00
for _ , protocol := range c . DestOverride {
switch strings . ToLower ( protocol ) {
case "http" :
2026-04-14 02:08:51 +08:00
protocols = append ( protocols , "http" )
2026-04-14 01:39:53 +08:00
case "tls" , "https" , "ssl" :
2026-04-14 02:08:51 +08:00
protocols = append ( protocols , "tls" )
2026-04-14 01:39:53 +08:00
case "quic" :
2026-04-14 02:08:51 +08:00
protocols = append ( protocols , "quic" )
2026-04-14 01:39:53 +08:00
case "fakedns" , "fakedns+others" :
2026-04-14 02:08:51 +08:00
protocols = append ( protocols , "fakedns" )
2026-04-14 01:39:53 +08:00
default :
return nil , errors . New ( "unknown protocol: " , protocol )
2020-11-25 19:01:53 +08:00
}
}
2026-04-14 02:08:51 +08:00
domains , err := geodata . ParseDomainRules ( c . DomainsExcluded , geodata . Domain_Substr )
if err != nil {
return nil , err
}
ips , err := geodata . ParseIPRules ( c . IPsExcluded )
2026-04-14 01:39:53 +08:00
if err != nil {
return nil , err
2021-01-22 04:50:09 +08:00
}
2020-11-25 19:01:53 +08:00
return & proxyman . SniffingConfig {
Enabled : c . Enabled ,
2026-04-14 02:08:51 +08:00
DestinationOverride : protocols ,
DomainsExcluded : domains ,
IpsExcluded : ips ,
2021-03-06 23:39:50 -05:00
MetadataOnly : c . MetadataOnly ,
2021-09-16 15:05:48 +08:00
RouteOnly : c . RouteOnly ,
2020-11-25 19:01:53 +08:00
}, nil
}
type MuxConfig struct {
2023-04-14 22:51:18 +00:00
Enabled bool `json:"enabled"`
Concurrency int16 `json:"concurrency"`
XudpConcurrency int16 `json:"xudpConcurrency"`
XudpProxyUDP443 string `json:"xudpProxyUDP443"`
2020-11-25 19:01:53 +08:00
}
// Build creates MultiplexingConfig, Concurrency < 0 completely disables mux.
2023-04-10 10:15:16 +08:00
func ( m * MuxConfig ) Build () ( * proxyman . MultiplexingConfig , error ) {
2023-04-14 22:51:18 +00:00
switch m . XudpProxyUDP443 {
case "" :
m . XudpProxyUDP443 = "reject"
case "reject" , "allow" , "skip" :
default :
2024-06-29 14:32:57 -04:00
return nil , errors . New ( `unknown "xudpProxyUDP443": ` , m . XudpProxyUDP443 )
2023-04-14 22:51:18 +00:00
}
2023-04-10 10:36:07 +08:00
return & proxyman . MultiplexingConfig {
Enabled : m . Enabled ,
Concurrency : int32 ( m . Concurrency ),
XudpConcurrency : int32 ( m . XudpConcurrency ),
2023-04-14 22:51:18 +00:00
XudpProxyUDP443 : m . XudpProxyUDP443 ,
2023-04-10 10:36:07 +08:00
}, nil
2020-11-25 19:01:53 +08:00
}
type InboundDetourConfig struct {
2026-01-13 21:31:51 +08:00
Protocol string `json:"protocol"`
PortList * PortList `json:"port"`
ListenOn * Address `json:"listen"`
Settings * json . RawMessage `json:"settings"`
Tag string `json:"tag"`
StreamSetting * StreamConfig `json:"streamSettings"`
SniffingConfig * SniffingConfig `json:"sniffing"`
2020-11-25 19:01:53 +08:00
}
// Build implements Buildable.
func ( c * InboundDetourConfig ) Build () ( * core . InboundHandlerConfig , error ) {
receiverSettings := & proxyman . ReceiverConfig {}
2026-01-12 10:01:27 +00:00
// TUN inbound doesn't need port configuration as it uses network interface instead
if strings . ToLower ( c . Protocol ) == "tun" {
// Skip port validation for TUN
2026-08-27 19:57:15 +08:00
} else if c . ListenOn == nil || len ( c . ListenOn . String ()) == 0 {
2021-11-12 18:12:32 +08:00
// Listen on anyip, must set PortList
if c . PortList == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Listen on AnyIP but no Port(s) set in InboundDetour." )
2020-11-25 19:01:53 +08:00
}
2021-11-12 18:12:32 +08:00
receiverSettings . PortList = c . PortList . Build ()
2020-11-25 19:01:53 +08:00
} else {
// Listen on specific IP or Unix Domain Socket
receiverSettings . Listen = c . ListenOn . Build ()
2023-12-22 17:42:04 +08:00
listenDS := c . ListenOn . Family (). IsDomain () && ( filepath . IsAbs ( c . ListenOn . Domain ()) || c . ListenOn . Domain ()[ 0 ] == '@' )
2020-11-25 19:01:53 +08:00
listenIP := c . ListenOn . Family (). IsIP () || ( c . ListenOn . Family (). IsDomain () && c . ListenOn . Domain () == "localhost" )
if listenIP {
2021-11-12 18:12:32 +08:00
// Listen on specific IP, must set PortList
if c . PortList == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Listen on specific ip without port in InboundDetour." )
2020-11-25 19:01:53 +08:00
}
// Listen on IP:Port
2021-11-12 18:12:32 +08:00
receiverSettings . PortList = c . PortList . Build ()
2020-11-25 19:01:53 +08:00
} else if listenDS {
2021-11-12 18:12:32 +08:00
if c . PortList != nil {
// Listen on Unix Domain Socket, PortList should be nil
receiverSettings . PortList = nil
2020-11-25 19:01:53 +08:00
}
} else {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "unable to listen on domain address: " , c . ListenOn . Domain ())
2020-11-25 19:01:53 +08:00
}
}
if c . StreamSetting != nil {
ss , err := c . StreamSetting . Build ()
if err != nil {
return nil , err
}
receiverSettings . StreamSettings = ss
2026-03-21 13:19:32 +00:00
if strings . Contains ( ss . SecurityType , "reality" ) && ( receiverSettings . PortList == nil ||
len ( receiverSettings . PortList . Ports ()) != 1 || receiverSettings . PortList . Ports ()[ 0 ] != 443 ) {
2026-07-17 21:51:29 +00:00
errors . LogWarning ( context . Background (), `REALITY: Listening on non-443 ports will increase the likelihood of your server's IP being blocked by the GFW` )
2026-03-21 13:19:32 +00:00
}
2020-11-25 19:01:53 +08:00
}
if c . SniffingConfig != nil {
s , err := c . SniffingConfig . Build ()
if err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "failed to build sniffing config" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
receiverSettings . SniffingSettings = s
}
settings := [] byte ( "{}" )
if c . Settings != nil {
settings = ([] byte )( * c . Settings )
}
rawConfig , err := inboundConfigLoader . LoadWithID ( settings , c . Protocol )
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to load inbound detour config for protocol " , c . Protocol ). Base ( err )
2020-11-25 19:01:53 +08:00
}
if dokodemoConfig , ok := rawConfig .( * DokodemoConfig ); ok {
2025-08-01 11:20:53 +00:00
receiverSettings . ReceiveOriginalDestination = dokodemoConfig . FollowRedirect
2020-11-25 19:01:53 +08:00
}
ts , err := rawConfig .( Buildable ). Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build inbound handler for protocol " , c . Protocol ). Base ( err )
2020-11-25 19:01:53 +08:00
}
2026-09-27 22:46:54 +03:30
if _ , ok := ts .( * masque . ServerConfig ); ! ok && receiverSettings . StreamSettings != nil && receiverSettings . StreamSettings . ProtocolName == "masque" {
return nil , errors . New ( "the masque transport can only be used by the masque inbound" )
}
2020-11-25 19:01:53 +08:00
return & core . InboundHandlerConfig {
Tag : c . Tag ,
ReceiverSettings : serial . ToTypedMessage ( receiverSettings ),
ProxySettings : serial . ToTypedMessage ( ts ),
}, nil
}
type OutboundDetourConfig struct {
2025-08-15 22:51:36 +02:00
Protocol string `json:"protocol"`
SendThrough * string `json:"sendThrough"`
Tag string `json:"tag"`
Settings * json . RawMessage `json:"settings"`
StreamSetting * StreamConfig `json:"streamSettings"`
2026-09-08 11:31:25 +08:00
ProxySettings * json . RawMessage `json:"proxySettings"`
2025-08-15 22:51:36 +02:00
MuxSettings * MuxConfig `json:"mux"`
TargetStrategy string `json:"targetStrategy"`
2020-11-25 19:01:53 +08:00
}
2026-07-07 10:01:53 +08:00
func requiresTransportSecurity ( address * Address ) bool {
if address == nil || address . Address == nil {
return false
}
if address . Family (). IsIP () {
return ! geodata . GetPrivateIPMatcher (). Match ( address . IP ())
}
domain := strings . TrimSuffix ( strings . ToLower ( address . Domain ()), "." )
return ! geodata . GetPrivateDomainMatcher (). MatchAny ( domain )
}
func validateOutboundTransportSecurity ( rawConfig interface {}, senderSettings * proxyman . SenderConfig ) error {
if senderSettings . StreamSettings != nil && senderSettings . StreamSettings . GetSecurityType () != "" {
return nil
}
if vlessCfg , ok := rawConfig .( * VLessOutboundConfig ); ok {
if vlessCfg . Encryption != "" && vlessCfg . Encryption != "none" {
return nil
}
2026-09-09 02:18:12 +08:00
if requiresTransportSecurity ( vlessCfg . Address ) {
2026-07-07 10:01:53 +08:00
return errors . New ( "vless without TLS or other encryption is prohibited unless the server address is a private IP or domain" )
}
}
if tjCfg , ok := rawConfig .( * TrojanClientConfig ); ok {
2026-08-27 19:57:15 +08:00
if requiresTransportSecurity ( tjCfg . Servers [ 0 ]. Address ) {
2026-07-07 10:01:53 +08:00
return errors . New ( "trojan without TLS is prohibited unless the server address is a private IP or domain" )
}
}
return nil
}
2020-11-25 19:01:53 +08:00
// Build implements Buildable.
func ( c * OutboundDetourConfig ) Build () ( * core . OutboundHandlerConfig , error ) {
2026-09-08 11:31:25 +08:00
if c . ProxySettings != nil {
return nil , errors . PrintRemovedFeatureError ( `outbound "proxySettings"` , `"streamSettings.sockopt.dialerProxy"` )
}
2020-11-25 19:01:53 +08:00
senderSettings := & proxyman . SenderConfig {}
2025-08-15 22:51:36 +02:00
switch strings . ToLower ( c . TargetStrategy ) {
case "asis" , "" :
senderSettings . TargetStrategy = internet . DomainStrategy_AS_IS
case "useip" :
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP
case "useipv4" :
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP4
case "useipv6" :
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP6
case "useipv4v6" :
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP46
case "useipv6v4" :
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP64
case "forceip" :
senderSettings . TargetStrategy = internet . DomainStrategy_FORCE_IP
case "forceipv4" :
senderSettings . TargetStrategy = internet . DomainStrategy_FORCE_IP4
case "forceipv6" :
senderSettings . TargetStrategy = internet . DomainStrategy_FORCE_IP6
case "forceipv4v6" :
senderSettings . TargetStrategy = internet . DomainStrategy_FORCE_IP46
case "forceipv6v4" :
senderSettings . TargetStrategy = internet . DomainStrategy_FORCE_IP64
default :
return nil , errors . New ( "unsupported target domain strategy: " , c . TargetStrategy )
}
2020-11-25 19:01:53 +08:00
if c . SendThrough != nil {
2024-03-21 10:15:07 +00:00
address := ParseSendThough ( c . SendThrough )
2026-05-29 23:04:59 +08:00
// Check if CIDR exists
2024-03-21 10:15:07 +00:00
if strings . Contains ( * c . SendThrough , "/" ) {
senderSettings . ViaCidr = strings . Split ( * c . SendThrough , "/" )[ 1 ]
} else {
if address . Family (). IsDomain () {
2025-06-06 10:54:15 +09:00
domain := address . Address . Domain ()
if domain != "origin" && domain != "srcip" {
2025-02-21 00:15:59 +04:00
return nil , errors . New ( "unable to send through: " + address . String ())
}
2024-03-21 10:15:07 +00:00
}
2020-11-25 19:01:53 +08:00
}
senderSettings . Via = address . Build ()
}
if c . StreamSetting != nil {
ss , err := c . StreamSetting . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build stream settings for outbound detour" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
senderSettings . StreamSettings = ss
}
if c . MuxSettings != nil {
2023-04-10 10:15:16 +08:00
ms , err := c . MuxSettings . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build Mux config" ). Base ( err )
2023-04-10 10:15:16 +08:00
}
senderSettings . MultiplexSettings = ms
2020-11-25 19:01:53 +08:00
}
settings := [] byte ( "{}" )
if c . Settings != nil {
settings = ([] byte )( * c . Settings )
}
rawConfig , err := outboundConfigLoader . LoadWithID ( settings , c . Protocol )
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to load outbound detour config for protocol " , c . Protocol ). Base ( err )
2020-11-25 19:01:53 +08:00
}
ts , err := rawConfig .( Buildable ). Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build outbound handler for protocol " , c . Protocol ). Base ( err )
2020-11-25 19:01:53 +08:00
}
2026-08-27 19:57:15 +08:00
if err := validateOutboundTransportSecurity ( rawConfig , senderSettings ); err != nil {
return nil , err
}
2020-11-25 19:01:53 +08:00
2026-09-24 10:13:37 +08:00
if _ , ok := ts .( * masque . ClientConfig ); ok {
if ms := senderSettings . MultiplexSettings ; ms != nil && ms . Enabled {
return nil , errors . New ( `masque outbound does not support "mux"` )
}
} else if senderSettings . StreamSettings != nil && senderSettings . StreamSettings . ProtocolName == "masque" {
return nil , errors . New ( "the masque transport can only be used by the masque outbound" )
}
2026-09-08 11:31:25 +08:00
if fc , ok := ts .( * freedom . Config ); ok {
if senderSettings . StreamSettings != nil &&
senderSettings . StreamSettings . SocketSettings != nil &&
senderSettings . StreamSettings . SocketSettings . AddressPortStrategy != internet . AddressPortStrategy_None {
return nil , errors . New ( `freedom outbound does not support "sockopt.addressPortStrategy"` )
}
var strategy internet . DomainStrategy
if strategy = senderSettings . TargetStrategy ; strategy != internet . DomainStrategy_AS_IS {
errors . LogWarning ( context . Background (), `The "outbound.targetStrategy" setting is not supported directly by freedom and has been automatically migrated to "sockopt.domainStrategy" with no behavior change.` )
senderSettings . TargetStrategy = internet . DomainStrategy_AS_IS
} else if strategy = fc . DomainStrategy ; strategy != internet . DomainStrategy_AS_IS {
errors . LogWarning ( context . Background (), `The "freedom.domainStrategy" setting is deprecated and will be removed. For compatibility, its value has been automatically migrated to "sockopt.domainStrategy". Please update your config before removal.` )
}
if strategy != internet . DomainStrategy_AS_IS {
if senderSettings . StreamSettings == nil {
senderSettings . StreamSettings = & internet . StreamConfig {}
}
if senderSettings . StreamSettings . SocketSettings == nil {
senderSettings . StreamSettings . SocketSettings = & internet . SocketConfig {}
}
senderSettings . StreamSettings . SocketSettings . DomainStrategy = strategy
}
}
2020-11-25 19:01:53 +08:00
return & core . OutboundHandlerConfig {
SenderSettings : serial . ToTypedMessage ( senderSettings ),
Tag : c . Tag ,
ProxySettings : serial . ToTypedMessage ( ts ),
}, nil
}
type StatsConfig struct {}
// Build implements Buildable.
func ( c * StatsConfig ) Build () ( * stats . Config , error ) {
return & stats . Config {}, nil
}
2026-07-10 10:56:49 +08:00
type EnvConfig map [ string ] string
func ( c EnvConfig ) Override ( o EnvConfig ) {
for key , value := range o {
c [ key ] = value
}
}
2020-11-25 19:01:53 +08:00
type Config struct {
2024-09-06 06:31:26 +08:00
// Deprecated: Global transport config is no longer used
// left for returning error
2024-11-09 14:16:11 +03:00
Transport map [ string ] json . RawMessage `json:"transport"`
2024-09-06 06:31:26 +08:00
2026-07-10 10:56:49 +08:00
Env EnvConfig `json:"env"`
2024-03-21 10:15:07 +00:00
LogConfig * LogConfig `json:"log"`
RouterConfig * RouterConfig `json:"routing"`
DNSConfig * DNSConfig `json:"dns"`
InboundConfigs [] InboundDetourConfig `json:"inbounds"`
OutboundConfigs [] OutboundDetourConfig `json:"outbounds"`
Policy * PolicyConfig `json:"policy"`
API * APIConfig `json:"api"`
Metrics * MetricsConfig `json:"metrics"`
Stats * StatsConfig `json:"stats"`
Reverse * ReverseConfig `json:"reverse"`
FakeDNS * FakeDNSConfig `json:"fakeDns"`
Observatory * ObservatoryConfig `json:"observatory"`
2024-02-17 22:51:37 -05:00
BurstObservatory * BurstObservatoryConfig `json:"burstObservatory"`
2025-08-03 03:06:47 +02:00
Version * VersionConfig `json:"version"`
2026-04-26 05:20:42 +08:00
Geodata * GeodataConfig `json:"geodata"`
2020-11-25 19:01:53 +08:00
}
func ( c * Config ) findInboundTag ( tag string ) int {
found := - 1
for idx , ib := range c . InboundConfigs {
if ib . Tag == tag {
found = idx
break
}
}
return found
}
func ( c * Config ) findOutboundTag ( tag string ) int {
found := - 1
for idx , ob := range c . OutboundConfigs {
if ob . Tag == tag {
found = idx
break
}
}
return found
}
// Override method accepts another Config overrides the current attribute
func ( c * Config ) Override ( o * Config , fn string ) {
// only process the non-deprecated members
if o . LogConfig != nil {
c . LogConfig = o . LogConfig
}
if o . RouterConfig != nil {
c . RouterConfig = o . RouterConfig
}
if o . DNSConfig != nil {
c . DNSConfig = o . DNSConfig
}
if o . Transport != nil {
c . Transport = o . Transport
}
2026-07-10 10:56:49 +08:00
if o . Env != nil {
if c . Env == nil {
c . Env = EnvConfig {}
}
c . Env . Override ( o . Env )
}
2020-11-25 19:01:53 +08:00
if o . Policy != nil {
c . Policy = o . Policy
}
if o . API != nil {
c . API = o . API
}
2022-03-25 11:24:23 +08:00
if o . Metrics != nil {
c . Metrics = o . Metrics
}
2020-11-25 19:01:53 +08:00
if o . Stats != nil {
c . Stats = o . Stats
}
if o . Reverse != nil {
c . Reverse = o . Reverse
}
2021-03-06 23:39:50 -05:00
if o . FakeDNS != nil {
c . FakeDNS = o . FakeDNS
}
2021-10-26 01:00:31 -04:00
if o . Observatory != nil {
c . Observatory = o . Observatory
}
2024-03-23 21:25:18 +08:00
if o . BurstObservatory != nil {
c . BurstObservatory = o . BurstObservatory
}
2025-08-03 03:06:47 +02:00
if o . Version != nil {
c . Version = o . Version
}
2026-04-26 05:20:42 +08:00
if o . Geodata != nil {
c . Geodata = o . Geodata
}
2023-11-06 03:49:08 +09:00
// update the Inbound in slice if the only one in override config has same tag
2020-11-25 19:01:53 +08:00
if len ( o . InboundConfigs ) > 0 {
2023-10-21 15:43:37 +04:00
for i := range o . InboundConfigs {
if idx := c . findInboundTag ( o . InboundConfigs [ i ]. Tag ); idx > - 1 {
c . InboundConfigs [ idx ] = o . InboundConfigs [ i ]
2024-06-29 14:32:57 -04:00
errors . LogInfo ( context . Background (), "[" , fn , "] updated inbound with tag: " , o . InboundConfigs [ i ]. Tag )
2023-10-21 15:43:37 +04:00
2020-11-25 19:01:53 +08:00
} else {
2023-10-21 15:43:37 +04:00
c . InboundConfigs = append ( c . InboundConfigs , o . InboundConfigs [ i ])
2024-06-29 14:32:57 -04:00
errors . LogInfo ( context . Background (), "[" , fn , "] appended inbound with tag: " , o . InboundConfigs [ i ]. Tag )
2020-11-25 19:01:53 +08:00
}
}
}
2023-11-06 03:49:08 +09:00
// update the Outbound in slice if the only one in override config has same tag
2020-11-25 19:01:53 +08:00
if len ( o . OutboundConfigs ) > 0 {
2023-10-24 05:04:58 +03:30
outboundPrepends := [] OutboundDetourConfig {}
for i := range o . OutboundConfigs {
if idx := c . findOutboundTag ( o . OutboundConfigs [ i ]. Tag ); idx > - 1 {
c . OutboundConfigs [ idx ] = o . OutboundConfigs [ i ]
2024-06-29 14:32:57 -04:00
errors . LogInfo ( context . Background (), "[" , fn , "] updated outbound with tag: " , o . OutboundConfigs [ i ]. Tag )
2020-11-25 19:01:53 +08:00
} else {
if strings . Contains ( strings . ToLower ( fn ), "tail" ) {
2023-10-24 05:04:58 +03:30
c . OutboundConfigs = append ( c . OutboundConfigs , o . OutboundConfigs [ i ])
2024-06-29 14:32:57 -04:00
errors . LogInfo ( context . Background (), "[" , fn , "] appended outbound with tag: " , o . OutboundConfigs [ i ]. Tag )
2020-11-25 19:01:53 +08:00
} else {
2023-10-24 05:04:58 +03:30
outboundPrepends = append ( outboundPrepends , o . OutboundConfigs [ i ])
2024-06-29 14:32:57 -04:00
errors . LogInfo ( context . Background (), "[" , fn , "] prepend outbound with tag: " , o . OutboundConfigs [ i ]. Tag )
2020-11-25 19:01:53 +08:00
}
}
2023-10-24 05:04:58 +03:30
}
if ! strings . Contains ( strings . ToLower ( fn ), "tail" ) && len ( outboundPrepends ) > 0 {
c . OutboundConfigs = append ( outboundPrepends , c . OutboundConfigs ... )
2020-11-25 19:01:53 +08:00
}
}
}
// Build implements Buildable.
func ( c * Config ) Build () ( * core . Config , error ) {
2026-07-10 10:56:49 +08:00
for key , value := range c . Env {
if err := os . Setenv ( key , value ); err != nil {
return nil , errors . New ( "failed to apply environment configuration" ). Base ( err )
}
}
2021-03-06 23:39:50 -05:00
if err := PostProcessConfigureFile ( c ); err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to post-process configuration file" ). Base ( err )
2021-03-06 23:39:50 -05:00
}
2020-11-25 19:01:53 +08:00
config := & core . Config {
App : [] * serial . TypedMessage {
serial . ToTypedMessage ( & dispatcher . Config {}),
serial . ToTypedMessage ( & proxyman . InboundConfig {}),
serial . ToTypedMessage ( & proxyman . OutboundConfig {}),
},
}
if c . API != nil {
apiConf , err := c . API . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build API configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( apiConf ))
}
2022-03-25 11:24:23 +08:00
if c . Metrics != nil {
metricsConf , err := c . Metrics . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build metrics configuration" ). Base ( err )
2022-03-25 11:24:23 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( metricsConf ))
}
2020-11-25 19:01:53 +08:00
if c . Stats != nil {
statsConf , err := c . Stats . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build stats configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( statsConf ))
}
var logConfMsg * serial . TypedMessage
if c . LogConfig != nil {
logConfMsg = serial . ToTypedMessage ( c . LogConfig . Build ())
} else {
logConfMsg = serial . ToTypedMessage ( DefaultLogConfig ())
}
// let logger module be the first App to start,
// so that other modules could print log during initiating
config . App = append ([] * serial . TypedMessage { logConfMsg }, config . App ... )
if c . RouterConfig != nil {
routerConfig , err := c . RouterConfig . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build routing configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( routerConfig ))
}
if c . DNSConfig != nil {
dnsApp , err := c . DNSConfig . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build DNS configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( dnsApp ))
}
if c . Policy != nil {
pc , err := c . Policy . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build policy configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( pc ))
}
if c . Reverse != nil {
2026-04-25 23:03:19 +00:00
return nil , errors . PrintRemovedFeatureError ( `"legacy reverse"` , `"VLESS Reverse Proxy"` )
2020-11-25 19:01:53 +08:00
r , err := c . Reverse . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build reverse configuration" ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( r ))
}
2021-03-06 23:39:50 -05:00
if c . FakeDNS != nil {
r , err := c . FakeDNS . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build fake DNS configuration" ). Base ( err )
2021-03-06 23:39:50 -05:00
}
2022-04-23 19:24:46 -04:00
config . App = append ([] * serial . TypedMessage { serial . ToTypedMessage ( r )}, config . App ... )
2021-03-06 23:39:50 -05:00
}
2021-04-09 05:20:30 +08:00
if c . Observatory != nil {
r , err := c . Observatory . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build observatory configuration" ). Base ( err )
2021-04-09 05:20:30 +08:00
}
config . App = append ( config . App , serial . ToTypedMessage ( r ))
}
2024-02-17 22:51:37 -05:00
if c . BurstObservatory != nil {
r , err := c . BurstObservatory . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build burst observatory configuration" ). Base ( err )
2024-02-17 22:51:37 -05:00
}
config . App = append ( config . App , serial . ToTypedMessage ( r ))
}
2025-08-03 03:06:47 +02:00
if c . Version != nil {
r , err := c . Version . Build ()
if err != nil {
return nil , errors . New ( "failed to build version configuration" ). Base ( err )
}
config . App = append ( config . App , serial . ToTypedMessage ( r ))
}
2026-04-26 05:20:42 +08:00
if c . Geodata != nil {
r , err := c . Geodata . Build ()
if err != nil {
return nil , errors . New ( "failed to build geodata configuration" ). Base ( err )
}
config . App = append ( config . App , serial . ToTypedMessage ( r ))
}
2020-11-25 19:01:53 +08:00
var inbounds [] InboundDetourConfig
if len ( c . InboundConfigs ) > 0 {
inbounds = append ( inbounds , c . InboundConfigs ... )
}
2024-09-06 06:31:26 +08:00
if len ( c . Transport ) > 0 {
2024-09-15 12:55:54 +08:00
return nil , errors . PrintRemovedFeatureError ( "Global transport config" , "streamSettings in inbounds and outbounds" )
2024-09-06 06:31:26 +08:00
}
2020-11-25 19:01:53 +08:00
for _ , rawInboundConfig := range inbounds {
ic , err := rawInboundConfig . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build inbound config with tag " , rawInboundConfig . Tag ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . Inbound = append ( config . Inbound , ic )
}
var outbounds [] OutboundDetourConfig
if len ( c . OutboundConfigs ) > 0 {
outbounds = append ( outbounds , c . OutboundConfigs ... )
}
for _ , rawOutboundConfig := range outbounds {
oc , err := rawOutboundConfig . Build ()
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "failed to build outbound config with tag " , rawOutboundConfig . Tag ). Base ( err )
2020-11-25 19:01:53 +08:00
}
config . Outbound = append ( config . Outbound , oc )
}
return config , nil
}
2024-03-21 10:15:07 +00:00
// Convert string to Address.
func ParseSendThough ( Addr * string ) * Address {
var addr Address
addr . Address = net . ParseAddress ( strings . Split ( * Addr , "/" )[ 0 ])
return & addr
}