123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- package conf
- import (
- "encoding/json"
- "github.com/xtls/xray-core/transport/internet"
- "log"
- "os"
- "strings"
- "github.com/xtls/xray-core/app/dispatcher"
- "github.com/xtls/xray-core/app/proxyman"
- "github.com/xtls/xray-core/app/stats"
- "github.com/xtls/xray-core/common/serial"
- core "github.com/xtls/xray-core/core"
- "github.com/xtls/xray-core/transport/internet/xtls"
- )
- var (
- inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
- "dokodemo-door": func() interface{} { return new(DokodemoConfig) },
- "http": func() interface{} { return new(HTTPServerConfig) },
- "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
- "socks": func() interface{} { return new(SocksServerConfig) },
- "vless": func() interface{} { return new(VLessInboundConfig) },
- "vmess": func() interface{} { return new(VMessInboundConfig) },
- "trojan": func() interface{} { return new(TrojanServerConfig) },
- "mtproto": func() interface{} { return new(MTProtoServerConfig) },
- }, "protocol", "settings")
- outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
- "blackhole": func() interface{} { return new(BlackholeConfig) },
- "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) },
- "mtproto": func() interface{} { return new(MTProtoClientConfig) },
- "dns": func() interface{} { return new(DNSOutboundConfig) },
- }, "protocol", "settings")
- ctllog = log.New(os.Stderr, "xctl> ", 0)
- )
- func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) {
- kp := make([]proxyman.KnownProtocols, 0, 8)
- for _, p := range s {
- switch strings.ToLower(p) {
- case "http":
- kp = append(kp, proxyman.KnownProtocols_HTTP)
- case "https", "tls", "ssl":
- kp = append(kp, proxyman.KnownProtocols_TLS)
- default:
- return nil, newError("Unknown protocol: ", p)
- }
- }
- return kp, nil
- }
- type SniffingConfig struct {
- Enabled bool `json:"enabled"`
- DestOverride *StringList `json:"destOverride"`
- DomainsExcluded *StringList `json:"domainsExcluded"`
- MetadataOnly bool `json:"metadataOnly"`
- }
- // Build implements Buildable.
- func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
- var p []string
- if c.DestOverride != nil {
- for _, protocol := range *c.DestOverride {
- switch strings.ToLower(protocol) {
- case "http":
- p = append(p, "http")
- case "tls", "https", "ssl":
- p = append(p, "tls")
- case "fakedns":
- p = append(p, "fakedns")
- default:
- return nil, newError("unknown protocol: ", protocol)
- }
- }
- }
- var d []string
- if c.DomainsExcluded != nil {
- for _, domain := range *c.DomainsExcluded {
- d = append(d, strings.ToLower(domain))
- }
- }
- return &proxyman.SniffingConfig{
- Enabled: c.Enabled,
- DestinationOverride: p,
- DomainsExcluded: d,
- MetadataOnly: c.MetadataOnly,
- }, nil
- }
- type MuxConfig struct {
- Enabled bool `json:"enabled"`
- Concurrency int16 `json:"concurrency"`
- }
- // Build creates MultiplexingConfig, Concurrency < 0 completely disables mux.
- func (m *MuxConfig) Build() *proxyman.MultiplexingConfig {
- if m.Concurrency < 0 {
- return nil
- }
- var con uint32 = 8
- if m.Concurrency > 0 {
- con = uint32(m.Concurrency)
- }
- return &proxyman.MultiplexingConfig{
- Enabled: m.Enabled,
- Concurrency: con,
- }
- }
- type InboundDetourAllocationConfig struct {
- Strategy string `json:"strategy"`
- Concurrency *uint32 `json:"concurrency"`
- RefreshMin *uint32 `json:"refresh"`
- }
- // Build implements Buildable.
- func (c *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) {
- config := new(proxyman.AllocationStrategy)
- switch strings.ToLower(c.Strategy) {
- case "always":
- config.Type = proxyman.AllocationStrategy_Always
- case "random":
- config.Type = proxyman.AllocationStrategy_Random
- case "external":
- config.Type = proxyman.AllocationStrategy_External
- default:
- return nil, newError("unknown allocation strategy: ", c.Strategy)
- }
- if c.Concurrency != nil {
- config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
- Value: *c.Concurrency,
- }
- }
- if c.RefreshMin != nil {
- config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{
- Value: *c.RefreshMin,
- }
- }
- return config, nil
- }
- type InboundDetourConfig struct {
- Protocol string `json:"protocol"`
- PortRange *PortRange `json:"port"`
- ListenOn *Address `json:"listen"`
- Settings *json.RawMessage `json:"settings"`
- Tag string `json:"tag"`
- Allocation *InboundDetourAllocationConfig `json:"allocate"`
- StreamSetting *StreamConfig `json:"streamSettings"`
- DomainOverride *StringList `json:"domainOverride"`
- SniffingConfig *SniffingConfig `json:"sniffing"`
- }
- // Build implements Buildable.
- func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
- receiverSettings := &proxyman.ReceiverConfig{}
- if c.ListenOn == nil {
- // Listen on anyip, must set PortRange
- if c.PortRange == nil {
- return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.")
- }
- receiverSettings.PortRange = c.PortRange.Build()
- } else {
- // Listen on specific IP or Unix Domain Socket
- receiverSettings.Listen = c.ListenOn.Build()
- listenDS := c.ListenOn.Family().IsDomain() && (c.ListenOn.Domain()[0] == '/' || c.ListenOn.Domain()[0] == '@')
- listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost")
- if listenIP {
- // Listen on specific IP, must set PortRange
- if c.PortRange == nil {
- return nil, newError("Listen on specific ip without port in InboundDetour.")
- }
- // Listen on IP:Port
- receiverSettings.PortRange = c.PortRange.Build()
- } else if listenDS {
- if c.PortRange != nil {
- // Listen on Unix Domain Socket, PortRange should be nil
- receiverSettings.PortRange = nil
- }
- } else {
- return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain())
- }
- }
- if c.Allocation != nil {
- concurrency := -1
- if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" {
- concurrency = int(*c.Allocation.Concurrency)
- }
- portRange := int(c.PortRange.To - c.PortRange.From + 1)
- if concurrency >= 0 && concurrency >= portRange {
- return nil, newError("not enough ports. concurrency = ", concurrency, " ports: ", c.PortRange.From, " - ", c.PortRange.To)
- }
- as, err := c.Allocation.Build()
- if err != nil {
- return nil, err
- }
- receiverSettings.AllocationStrategy = as
- }
- if c.StreamSetting != nil {
- ss, err := c.StreamSetting.Build()
- if err != nil {
- return nil, err
- }
- if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") {
- return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.")
- }
- receiverSettings.StreamSettings = ss
- }
- if c.SniffingConfig != nil {
- s, err := c.SniffingConfig.Build()
- if err != nil {
- return nil, newError("failed to build sniffing config").Base(err)
- }
- receiverSettings.SniffingSettings = s
- }
- if c.DomainOverride != nil {
- kp, err := toProtocolList(*c.DomainOverride)
- if err != nil {
- return nil, newError("failed to parse inbound detour config").Base(err)
- }
- receiverSettings.DomainOverride = kp
- }
- settings := []byte("{}")
- if c.Settings != nil {
- settings = ([]byte)(*c.Settings)
- }
- rawConfig, err := inboundConfigLoader.LoadWithID(settings, c.Protocol)
- if err != nil {
- return nil, newError("failed to load inbound detour config.").Base(err)
- }
- if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
- receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
- }
- ts, err := rawConfig.(Buildable).Build()
- if err != nil {
- return nil, err
- }
- return &core.InboundHandlerConfig{
- Tag: c.Tag,
- ReceiverSettings: serial.ToTypedMessage(receiverSettings),
- ProxySettings: serial.ToTypedMessage(ts),
- }, nil
- }
- type OutboundDetourConfig struct {
- Protocol string `json:"protocol"`
- SendThrough *Address `json:"sendThrough"`
- Tag string `json:"tag"`
- Settings *json.RawMessage `json:"settings"`
- StreamSetting *StreamConfig `json:"streamSettings"`
- ProxySettings *ProxyConfig `json:"proxySettings"`
- MuxSettings *MuxConfig `json:"mux"`
- }
- 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 {
- return newError("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning()
- }
- return nil
- }
- // Build implements Buildable.
- func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
- senderSettings := &proxyman.SenderConfig{}
- if err := c.checkChainProxyConfig(); err != nil {
- return nil, err
- }
- if c.SendThrough != nil {
- address := c.SendThrough
- if address.Family().IsDomain() {
- return nil, newError("unable to send through: " + address.String())
- }
- senderSettings.Via = address.Build()
- }
- if c.StreamSetting != nil {
- ss, err := c.StreamSetting.Build()
- if err != nil {
- return nil, err
- }
- if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") {
- return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.")
- }
- senderSettings.StreamSettings = ss
- }
- if c.ProxySettings != nil {
- ps, err := c.ProxySettings.Build()
- if err != nil {
- return nil, newError("invalid outbound detour proxy settings.").Base(err)
- }
- 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
- }
- senderSettings.ProxySettings = ps
- }
- if c.MuxSettings != nil {
- ms := c.MuxSettings.Build()
- if ms != nil && ms.Enabled {
- if ss := senderSettings.StreamSettings; ss != nil {
- if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) {
- return nil, newError("XTLS doesn't support Mux for now.")
- }
- }
- }
- senderSettings.MultiplexSettings = ms
- }
- settings := []byte("{}")
- if c.Settings != nil {
- settings = ([]byte)(*c.Settings)
- }
- rawConfig, err := outboundConfigLoader.LoadWithID(settings, c.Protocol)
- if err != nil {
- return nil, newError("failed to parse to outbound detour config.").Base(err)
- }
- ts, err := rawConfig.(Buildable).Build()
- if err != nil {
- return nil, err
- }
- 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 {
- // Port of this Point server.
- // Deprecated: Port exists for historical compatibility
- // and should not be used.
- Port uint16 `json:"port"`
- // Deprecated: InboundConfig exists for historical compatibility
- // and should not be used.
- InboundConfig *InboundDetourConfig `json:"inbound"`
- // Deprecated: OutboundConfig exists for historical compatibility
- // and should not be used.
- OutboundConfig *OutboundDetourConfig `json:"outbound"`
- // Deprecated: InboundDetours exists for historical compatibility
- // and should not be used.
- InboundDetours []InboundDetourConfig `json:"inboundDetour"`
- // Deprecated: OutboundDetours exists for historical compatibility
- // and should not be used.
- OutboundDetours []OutboundDetourConfig `json:"outboundDetour"`
- LogConfig *LogConfig `json:"log"`
- RouterConfig *RouterConfig `json:"routing"`
- DNSConfig *DNSConfig `json:"dns"`
- InboundConfigs []InboundDetourConfig `json:"inbounds"`
- OutboundConfigs []OutboundDetourConfig `json:"outbounds"`
- Transport *TransportConfig `json:"transport"`
- Policy *PolicyConfig `json:"policy"`
- API *APIConfig `json:"api"`
- Stats *StatsConfig `json:"stats"`
- Reverse *ReverseConfig `json:"reverse"`
- FakeDNS *FakeDNSConfig `json:"fakeDns"`
- }
- 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
- }
- if o.Stats != nil {
- c.Stats = o.Stats
- }
- if o.Reverse != nil {
- c.Reverse = o.Reverse
- }
- if o.FakeDNS != nil {
- c.FakeDNS = o.FakeDNS
- }
- // deprecated attrs... keep them for now
- if o.InboundConfig != nil {
- c.InboundConfig = o.InboundConfig
- }
- if o.OutboundConfig != nil {
- c.OutboundConfig = o.OutboundConfig
- }
- if o.InboundDetours != nil {
- c.InboundDetours = o.InboundDetours
- }
- if o.OutboundDetours != nil {
- c.OutboundDetours = o.OutboundDetours
- }
- // deprecated attrs
- // update the Inbound in slice if the only one in overide config has same tag
- if len(o.InboundConfigs) > 0 {
- if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 {
- if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 {
- c.InboundConfigs[idx] = o.InboundConfigs[0]
- ctllog.Println("[", fn, "] updated inbound with tag: ", o.InboundConfigs[0].Tag)
- } else {
- c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0])
- ctllog.Println("[", fn, "] appended inbound with tag: ", o.InboundConfigs[0].Tag)
- }
- } else {
- c.InboundConfigs = o.InboundConfigs
- }
- }
- // update the Outbound in slice if the only one in overide config has same tag
- if len(o.OutboundConfigs) > 0 {
- if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 {
- if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 {
- c.OutboundConfigs[idx] = o.OutboundConfigs[0]
- ctllog.Println("[", fn, "] updated outbound with tag: ", o.OutboundConfigs[0].Tag)
- } else {
- if strings.Contains(strings.ToLower(fn), "tail") {
- c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0])
- ctllog.Println("[", fn, "] appended outbound with tag: ", o.OutboundConfigs[0].Tag)
- } else {
- c.OutboundConfigs = append(o.OutboundConfigs, c.OutboundConfigs...)
- ctllog.Println("[", fn, "] prepended outbound with tag: ", o.OutboundConfigs[0].Tag)
- }
- }
- } else {
- c.OutboundConfigs = o.OutboundConfigs
- }
- }
- }
- func applyTransportConfig(s *StreamConfig, t *TransportConfig) {
- if s.TCPSettings == nil {
- s.TCPSettings = t.TCPConfig
- }
- if s.KCPSettings == nil {
- s.KCPSettings = t.KCPConfig
- }
- if s.WSSettings == nil {
- s.WSSettings = t.WSConfig
- }
- if s.HTTPSettings == nil {
- s.HTTPSettings = t.HTTPConfig
- }
- if s.DSSettings == nil {
- s.DSSettings = t.DSConfig
- }
- }
- // Build implements Buildable.
- func (c *Config) Build() (*core.Config, error) {
- if err := PostProcessConfigureFile(c); err != nil {
- return nil, err
- }
- 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 {
- return nil, err
- }
- config.App = append(config.App, serial.ToTypedMessage(apiConf))
- }
- if c.Stats != nil {
- statsConf, err := c.Stats.Build()
- if err != nil {
- return nil, err
- }
- 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 {
- return nil, err
- }
- config.App = append(config.App, serial.ToTypedMessage(routerConfig))
- }
- if c.DNSConfig != nil {
- dnsApp, err := c.DNSConfig.Build()
- if err != nil {
- return nil, newError("failed to parse DNS config").Base(err)
- }
- config.App = append(config.App, serial.ToTypedMessage(dnsApp))
- }
- if c.Policy != nil {
- pc, err := c.Policy.Build()
- if err != nil {
- return nil, err
- }
- config.App = append(config.App, serial.ToTypedMessage(pc))
- }
- if c.Reverse != nil {
- r, err := c.Reverse.Build()
- if err != nil {
- return nil, err
- }
- config.App = append(config.App, serial.ToTypedMessage(r))
- }
- if c.FakeDNS != nil {
- r, err := c.FakeDNS.Build()
- if err != nil {
- return nil, err
- }
- config.App = append(config.App, serial.ToTypedMessage(r))
- }
- var inbounds []InboundDetourConfig
- if c.InboundConfig != nil {
- inbounds = append(inbounds, *c.InboundConfig)
- }
- if len(c.InboundDetours) > 0 {
- inbounds = append(inbounds, c.InboundDetours...)
- }
- if len(c.InboundConfigs) > 0 {
- inbounds = append(inbounds, c.InboundConfigs...)
- }
- // Backward compatibility.
- if len(inbounds) > 0 && inbounds[0].PortRange == nil && c.Port > 0 {
- inbounds[0].PortRange = &PortRange{
- From: uint32(c.Port),
- To: uint32(c.Port),
- }
- }
- for _, rawInboundConfig := range inbounds {
- if c.Transport != nil {
- if rawInboundConfig.StreamSetting == nil {
- rawInboundConfig.StreamSetting = &StreamConfig{}
- }
- applyTransportConfig(rawInboundConfig.StreamSetting, c.Transport)
- }
- ic, err := rawInboundConfig.Build()
- if err != nil {
- return nil, err
- }
- config.Inbound = append(config.Inbound, ic)
- }
- var outbounds []OutboundDetourConfig
- if c.OutboundConfig != nil {
- outbounds = append(outbounds, *c.OutboundConfig)
- }
- if len(c.OutboundDetours) > 0 {
- outbounds = append(outbounds, c.OutboundDetours...)
- }
- if len(c.OutboundConfigs) > 0 {
- outbounds = append(outbounds, c.OutboundConfigs...)
- }
- for _, rawOutboundConfig := range outbounds {
- if c.Transport != nil {
- if rawOutboundConfig.StreamSetting == nil {
- rawOutboundConfig.StreamSetting = &StreamConfig{}
- }
- applyTransportConfig(rawOutboundConfig.StreamSetting, c.Transport)
- }
- oc, err := rawOutboundConfig.Build()
- if err != nil {
- return nil, err
- }
- config.Outbound = append(config.Outbound, oc)
- }
- return config, nil
- }
|