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"
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-05-19 17:48:21 +08:00
"github.com/xtls/xray-core/proxy/freedom"
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-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 ) } ,
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
} else if c . ListenOn == nil {
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 ) {
errors . LogWarning ( context . Background ( ) , ` REALITY: Listening on non-443 ports may get your IP blocked by the GFW ` )
}
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
}
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" `
ProxySettings * ProxyConfig ` json:"proxySettings" `
MuxSettings * MuxConfig ` json:"mux" `
TargetStrategy string ` json:"targetStrategy" `
2020-11-25 19:01:53 +08:00
}
2021-03-07 00:29:17 +08:00
func ( c * OutboundDetourConfig ) checkChainProxyConfig ( ) error {
if c . StreamSetting == nil || c . ProxySettings == nil || c . StreamSetting . SocketSettings == nil {
return nil
}
if len ( c . ProxySettings . Tag ) > 0 && len ( c . StreamSetting . SocketSettings . DialerProxy ) > 0 {
2024-06-29 14:32:57 -04:00
return errors . New ( "proxySettings.tag is conflicted with sockopt.dialerProxy" ) . AtWarning ( )
2021-03-07 00:29:17 +08:00
}
return nil
}
2020-11-25 19:01:53 +08:00
// Build implements Buildable.
func ( c * OutboundDetourConfig ) Build ( ) ( * core . OutboundHandlerConfig , error ) {
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 )
}
2021-03-07 00:29:17 +08:00
if err := c . checkChainProxyConfig ( ) ; err != nil {
return nil , err
}
2020-11-25 19:01:53 +08:00
if c . SendThrough != nil {
2024-03-21 10:15:07 +00:00
address := ParseSendThough ( c . SendThrough )
//Check if CIDR exists
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 . ProxySettings != nil {
ps , err := c . ProxySettings . Build ( )
if err != nil {
2025-03-31 03:50:25 +05:00
return nil , errors . New ( "invalid outbound detour proxy settings" ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
2021-03-07 00:29:17 +08:00
if ps . TransportLayerProxy {
if senderSettings . StreamSettings != nil {
if senderSettings . StreamSettings . SocketSettings != nil {
senderSettings . StreamSettings . SocketSettings . DialerProxy = ps . Tag
} else {
senderSettings . StreamSettings . SocketSettings = & internet . SocketConfig { DialerProxy : ps . Tag }
}
} else {
senderSettings . StreamSettings = & internet . StreamConfig { SocketSettings : & internet . SocketConfig { DialerProxy : ps . Tag } }
}
ps = nil
}
2020-11-25 19:01:53 +08:00
senderSettings . ProxySettings = ps
}
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-05-19 17:48:21 +08:00
if fc , ok := ts . ( * freedom . Config ) ; ok && fc . DomainStrategy != internet . DomainStrategy_AS_IS {
2026-05-20 03:56:56 +08:00
errors . PrintDeprecatedFeatureWarning ( "freedom.domainStrategy" , "sockopt.domainStrategy or targetStrategy" )
2026-05-19 17:48:21 +08:00
if c . ProxySettings != nil && ! c . ProxySettings . TransportLayerProxy {
2026-05-20 03:56:56 +08:00
if senderSettings . TargetStrategy == internet . DomainStrategy_AS_IS {
senderSettings . TargetStrategy = fc . DomainStrategy
}
} else {
if senderSettings . StreamSettings == nil {
senderSettings . StreamSettings = & internet . StreamConfig { }
}
if senderSettings . StreamSettings . SocketSettings == nil {
senderSettings . StreamSettings . SocketSettings = & internet . SocketConfig { }
}
senderSettings . StreamSettings . SocketSettings . DomainStrategy = fc . DomainStrategy
2026-05-19 17:48:21 +08:00
}
}
2026-05-19 19:06:34 +08:00
if fc , ok := rawConfig . ( * FreedomConfig ) ; ok && ( fc . PrivacyGuard == nil || * fc . PrivacyGuard ) {
if senderSettings . StreamSettings == nil {
senderSettings . StreamSettings = & internet . StreamConfig { }
}
if senderSettings . StreamSettings . SocketSettings == nil {
senderSettings . StreamSettings . SocketSettings = & internet . SocketConfig { }
}
if fc . PrivacyGuard == nil {
2026-05-20 03:56:56 +08:00
if c . ProxySettings != nil && ! c . ProxySettings . TransportLayerProxy {
if senderSettings . TargetStrategy == internet . DomainStrategy_AS_IS {
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP46
}
} else {
if senderSettings . TargetStrategy == internet . DomainStrategy_AS_IS &&
senderSettings . StreamSettings . SocketSettings . DomainStrategy == internet . DomainStrategy_AS_IS {
senderSettings . StreamSettings . SocketSettings . DomainStrategy = internet . DomainStrategy_USE_IP46
2026-05-19 19:06:34 +08:00
}
}
2026-05-20 03:56:56 +08:00
} else if * fc . PrivacyGuard {
2026-05-19 19:06:34 +08:00
if c . ProxySettings != nil && ! c . ProxySettings . TransportLayerProxy {
2026-05-20 03:56:56 +08:00
if senderSettings . TargetStrategy != internet . DomainStrategy_USE_IP4 &&
senderSettings . TargetStrategy != internet . DomainStrategy_USE_IP46 &&
senderSettings . TargetStrategy != internet . DomainStrategy_FORCE_IP4 &&
senderSettings . TargetStrategy != internet . DomainStrategy_FORCE_IP46 {
if senderSettings . TargetStrategy != internet . DomainStrategy_AS_IS {
errors . LogWarning ( context . Background ( ) , ` The "freedom" outbound "privacyGuard" overrides the existing "targetStrategy". Please update your config(s) if this is unintended. ` )
}
senderSettings . TargetStrategy = internet . DomainStrategy_USE_IP46
}
} else {
if senderSettings . StreamSettings . SocketSettings . DomainStrategy != internet . DomainStrategy_USE_IP4 &&
senderSettings . StreamSettings . SocketSettings . DomainStrategy != internet . DomainStrategy_USE_IP46 &&
senderSettings . StreamSettings . SocketSettings . DomainStrategy != internet . DomainStrategy_FORCE_IP4 &&
senderSettings . StreamSettings . SocketSettings . DomainStrategy != internet . DomainStrategy_FORCE_IP46 {
if senderSettings . StreamSettings . SocketSettings . DomainStrategy != internet . DomainStrategy_AS_IS {
2026-05-20 05:30:45 +08:00
errors . LogWarning ( context . Background ( ) , ` The "freedom" outbound "privacyGuard" overrides the existing "sockopt.domainStrategy". Please update your config(s) if this is unintended. ` )
2026-05-20 03:56:56 +08:00
}
senderSettings . StreamSettings . SocketSettings . DomainStrategy = internet . DomainStrategy_USE_IP46
}
if senderSettings . TargetStrategy != internet . DomainStrategy_AS_IS {
errors . LogWarning ( context . Background ( ) , ` The "freedom" outbound "privacyGuard" overrides the existing "targetStrategy". Please update your config(s) if this is unintended. ` )
senderSettings . TargetStrategy = internet . DomainStrategy_AS_IS
}
2026-05-19 19:06:34 +08:00
}
}
}
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
}
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
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
}
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-10-21 15:43:37 +04:00
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 ) {
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
}