github.com/eagleql/xray-core@v1.4.4/infra/conf/xray.go (about)

     1  package conf
     2  
     3  import (
     4  	"encoding/json"
     5  	"github.com/eagleql/xray-core/transport/internet"
     6  	"log"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/eagleql/xray-core/app/dispatcher"
    11  	"github.com/eagleql/xray-core/app/proxyman"
    12  	"github.com/eagleql/xray-core/app/stats"
    13  	"github.com/eagleql/xray-core/common/serial"
    14  	core "github.com/eagleql/xray-core/core"
    15  	"github.com/eagleql/xray-core/transport/internet/xtls"
    16  )
    17  
    18  var (
    19  	inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
    20  		"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
    21  		"http":          func() interface{} { return new(HTTPServerConfig) },
    22  		"shadowsocks":   func() interface{} { return new(ShadowsocksServerConfig) },
    23  		"socks":         func() interface{} { return new(SocksServerConfig) },
    24  		"vless":         func() interface{} { return new(VLessInboundConfig) },
    25  		"vmess":         func() interface{} { return new(VMessInboundConfig) },
    26  		"trojan":        func() interface{} { return new(TrojanServerConfig) },
    27  		"mtproto":       func() interface{} { return new(MTProtoServerConfig) },
    28  	}, "protocol", "settings")
    29  
    30  	outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
    31  		"blackhole":   func() interface{} { return new(BlackholeConfig) },
    32  		"freedom":     func() interface{} { return new(FreedomConfig) },
    33  		"http":        func() interface{} { return new(HTTPClientConfig) },
    34  		"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
    35  		"socks":       func() interface{} { return new(SocksClientConfig) },
    36  		"vless":       func() interface{} { return new(VLessOutboundConfig) },
    37  		"vmess":       func() interface{} { return new(VMessOutboundConfig) },
    38  		"trojan":      func() interface{} { return new(TrojanClientConfig) },
    39  		"mtproto":     func() interface{} { return new(MTProtoClientConfig) },
    40  		"dns":         func() interface{} { return new(DNSOutboundConfig) },
    41  	}, "protocol", "settings")
    42  
    43  	ctllog = log.New(os.Stderr, "xctl> ", 0)
    44  )
    45  
    46  func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) {
    47  	kp := make([]proxyman.KnownProtocols, 0, 8)
    48  	for _, p := range s {
    49  		switch strings.ToLower(p) {
    50  		case "http":
    51  			kp = append(kp, proxyman.KnownProtocols_HTTP)
    52  		case "https", "tls", "ssl":
    53  			kp = append(kp, proxyman.KnownProtocols_TLS)
    54  		default:
    55  			return nil, newError("Unknown protocol: ", p)
    56  		}
    57  	}
    58  	return kp, nil
    59  }
    60  
    61  type SniffingConfig struct {
    62  	Enabled         bool        `json:"enabled"`
    63  	DestOverride    *StringList `json:"destOverride"`
    64  	DomainsExcluded *StringList `json:"domainsExcluded"`
    65  	MetadataOnly    bool        `json:"metadataOnly"`
    66  }
    67  
    68  // Build implements Buildable.
    69  func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
    70  	var p []string
    71  	if c.DestOverride != nil {
    72  		for _, protocol := range *c.DestOverride {
    73  			switch strings.ToLower(protocol) {
    74  			case "http":
    75  				p = append(p, "http")
    76  			case "tls", "https", "ssl":
    77  				p = append(p, "tls")
    78  			case "fakedns":
    79  				p = append(p, "fakedns")
    80  			default:
    81  				return nil, newError("unknown protocol: ", protocol)
    82  			}
    83  		}
    84  	}
    85  
    86  	var d []string
    87  	if c.DomainsExcluded != nil {
    88  		for _, domain := range *c.DomainsExcluded {
    89  			d = append(d, strings.ToLower(domain))
    90  		}
    91  	}
    92  
    93  	return &proxyman.SniffingConfig{
    94  		Enabled:             c.Enabled,
    95  		DestinationOverride: p,
    96  		DomainsExcluded:     d,
    97  		MetadataOnly:        c.MetadataOnly,
    98  	}, nil
    99  }
   100  
   101  type MuxConfig struct {
   102  	Enabled     bool  `json:"enabled"`
   103  	Concurrency int16 `json:"concurrency"`
   104  }
   105  
   106  // Build creates MultiplexingConfig, Concurrency < 0 completely disables mux.
   107  func (m *MuxConfig) Build() *proxyman.MultiplexingConfig {
   108  	if m.Concurrency < 0 {
   109  		return nil
   110  	}
   111  
   112  	var con uint32 = 8
   113  	if m.Concurrency > 0 {
   114  		con = uint32(m.Concurrency)
   115  	}
   116  
   117  	return &proxyman.MultiplexingConfig{
   118  		Enabled:     m.Enabled,
   119  		Concurrency: con,
   120  	}
   121  }
   122  
   123  type InboundDetourAllocationConfig struct {
   124  	Strategy    string  `json:"strategy"`
   125  	Concurrency *uint32 `json:"concurrency"`
   126  	RefreshMin  *uint32 `json:"refresh"`
   127  }
   128  
   129  // Build implements Buildable.
   130  func (c *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) {
   131  	config := new(proxyman.AllocationStrategy)
   132  	switch strings.ToLower(c.Strategy) {
   133  	case "always":
   134  		config.Type = proxyman.AllocationStrategy_Always
   135  	case "random":
   136  		config.Type = proxyman.AllocationStrategy_Random
   137  	case "external":
   138  		config.Type = proxyman.AllocationStrategy_External
   139  	default:
   140  		return nil, newError("unknown allocation strategy: ", c.Strategy)
   141  	}
   142  	if c.Concurrency != nil {
   143  		config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
   144  			Value: *c.Concurrency,
   145  		}
   146  	}
   147  
   148  	if c.RefreshMin != nil {
   149  		config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{
   150  			Value: *c.RefreshMin,
   151  		}
   152  	}
   153  
   154  	return config, nil
   155  }
   156  
   157  type InboundDetourConfig struct {
   158  	Protocol       string                         `json:"protocol"`
   159  	PortRange      *PortRange                     `json:"port"`
   160  	ListenOn       *Address                       `json:"listen"`
   161  	Settings       *json.RawMessage               `json:"settings"`
   162  	Tag            string                         `json:"tag"`
   163  	Allocation     *InboundDetourAllocationConfig `json:"allocate"`
   164  	StreamSetting  *StreamConfig                  `json:"streamSettings"`
   165  	DomainOverride *StringList                    `json:"domainOverride"`
   166  	SniffingConfig *SniffingConfig                `json:"sniffing"`
   167  }
   168  
   169  // Build implements Buildable.
   170  func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
   171  	receiverSettings := &proxyman.ReceiverConfig{}
   172  
   173  	if c.ListenOn == nil {
   174  		// Listen on anyip, must set PortRange
   175  		if c.PortRange == nil {
   176  			return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.")
   177  		}
   178  		receiverSettings.PortRange = c.PortRange.Build()
   179  	} else {
   180  		// Listen on specific IP or Unix Domain Socket
   181  		receiverSettings.Listen = c.ListenOn.Build()
   182  		listenDS := c.ListenOn.Family().IsDomain() && (c.ListenOn.Domain()[0] == '/' || c.ListenOn.Domain()[0] == '@')
   183  		listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost")
   184  		if listenIP {
   185  			// Listen on specific IP, must set PortRange
   186  			if c.PortRange == nil {
   187  				return nil, newError("Listen on specific ip without port in InboundDetour.")
   188  			}
   189  			// Listen on IP:Port
   190  			receiverSettings.PortRange = c.PortRange.Build()
   191  		} else if listenDS {
   192  			if c.PortRange != nil {
   193  				// Listen on Unix Domain Socket, PortRange should be nil
   194  				receiverSettings.PortRange = nil
   195  			}
   196  		} else {
   197  			return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain())
   198  		}
   199  	}
   200  
   201  	if c.Allocation != nil {
   202  		concurrency := -1
   203  		if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" {
   204  			concurrency = int(*c.Allocation.Concurrency)
   205  		}
   206  		portRange := int(c.PortRange.To - c.PortRange.From + 1)
   207  		if concurrency >= 0 && concurrency >= portRange {
   208  			return nil, newError("not enough ports. concurrency = ", concurrency, " ports: ", c.PortRange.From, " - ", c.PortRange.To)
   209  		}
   210  
   211  		as, err := c.Allocation.Build()
   212  		if err != nil {
   213  			return nil, err
   214  		}
   215  		receiverSettings.AllocationStrategy = as
   216  	}
   217  	if c.StreamSetting != nil {
   218  		ss, err := c.StreamSetting.Build()
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  		if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") {
   223  			return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.")
   224  		}
   225  		receiverSettings.StreamSettings = ss
   226  	}
   227  	if c.SniffingConfig != nil {
   228  		s, err := c.SniffingConfig.Build()
   229  		if err != nil {
   230  			return nil, newError("failed to build sniffing config").Base(err)
   231  		}
   232  		receiverSettings.SniffingSettings = s
   233  	}
   234  	if c.DomainOverride != nil {
   235  		kp, err := toProtocolList(*c.DomainOverride)
   236  		if err != nil {
   237  			return nil, newError("failed to parse inbound detour config").Base(err)
   238  		}
   239  		receiverSettings.DomainOverride = kp
   240  	}
   241  
   242  	settings := []byte("{}")
   243  	if c.Settings != nil {
   244  		settings = ([]byte)(*c.Settings)
   245  	}
   246  	rawConfig, err := inboundConfigLoader.LoadWithID(settings, c.Protocol)
   247  	if err != nil {
   248  		return nil, newError("failed to load inbound detour config.").Base(err)
   249  	}
   250  	if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
   251  		receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
   252  	}
   253  	ts, err := rawConfig.(Buildable).Build()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	return &core.InboundHandlerConfig{
   259  		Tag:              c.Tag,
   260  		ReceiverSettings: serial.ToTypedMessage(receiverSettings),
   261  		ProxySettings:    serial.ToTypedMessage(ts),
   262  	}, nil
   263  }
   264  
   265  type OutboundDetourConfig struct {
   266  	Protocol      string           `json:"protocol"`
   267  	SendThrough   *Address         `json:"sendThrough"`
   268  	Tag           string           `json:"tag"`
   269  	Settings      *json.RawMessage `json:"settings"`
   270  	StreamSetting *StreamConfig    `json:"streamSettings"`
   271  	ProxySettings *ProxyConfig     `json:"proxySettings"`
   272  	MuxSettings   *MuxConfig       `json:"mux"`
   273  }
   274  
   275  func (c *OutboundDetourConfig) checkChainProxyConfig() error {
   276  	if c.StreamSetting == nil || c.ProxySettings == nil || c.StreamSetting.SocketSettings == nil {
   277  		return nil
   278  	}
   279  	if len(c.ProxySettings.Tag) > 0 && len(c.StreamSetting.SocketSettings.DialerProxy) > 0 {
   280  		return newError("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning()
   281  	}
   282  	return nil
   283  }
   284  
   285  // Build implements Buildable.
   286  func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
   287  	senderSettings := &proxyman.SenderConfig{}
   288  	if err := c.checkChainProxyConfig(); err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	if c.SendThrough != nil {
   293  		address := c.SendThrough
   294  		if address.Family().IsDomain() {
   295  			return nil, newError("unable to send through: " + address.String())
   296  		}
   297  		senderSettings.Via = address.Build()
   298  	}
   299  
   300  	if c.StreamSetting != nil {
   301  		ss, err := c.StreamSetting.Build()
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  		if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) && !strings.EqualFold(c.Protocol, "vless") && !strings.EqualFold(c.Protocol, "trojan") {
   306  			return nil, newError("XTLS doesn't supports " + c.Protocol + " for now.")
   307  		}
   308  		senderSettings.StreamSettings = ss
   309  	}
   310  
   311  	if c.ProxySettings != nil {
   312  		ps, err := c.ProxySettings.Build()
   313  		if err != nil {
   314  			return nil, newError("invalid outbound detour proxy settings.").Base(err)
   315  		}
   316  		if ps.TransportLayerProxy {
   317  			if senderSettings.StreamSettings != nil {
   318  				if senderSettings.StreamSettings.SocketSettings != nil {
   319  					senderSettings.StreamSettings.SocketSettings.DialerProxy = ps.Tag
   320  				} else {
   321  					senderSettings.StreamSettings.SocketSettings = &internet.SocketConfig{DialerProxy: ps.Tag}
   322  				}
   323  			} else {
   324  				senderSettings.StreamSettings = &internet.StreamConfig{SocketSettings: &internet.SocketConfig{DialerProxy: ps.Tag}}
   325  			}
   326  			ps = nil
   327  		}
   328  		senderSettings.ProxySettings = ps
   329  	}
   330  
   331  	if c.MuxSettings != nil {
   332  		ms := c.MuxSettings.Build()
   333  		if ms != nil && ms.Enabled {
   334  			if ss := senderSettings.StreamSettings; ss != nil {
   335  				if ss.SecurityType == serial.GetMessageType(&xtls.Config{}) {
   336  					return nil, newError("XTLS doesn't support Mux for now.")
   337  				}
   338  			}
   339  		}
   340  		senderSettings.MultiplexSettings = ms
   341  	}
   342  
   343  	settings := []byte("{}")
   344  	if c.Settings != nil {
   345  		settings = ([]byte)(*c.Settings)
   346  	}
   347  	rawConfig, err := outboundConfigLoader.LoadWithID(settings, c.Protocol)
   348  	if err != nil {
   349  		return nil, newError("failed to parse to outbound detour config.").Base(err)
   350  	}
   351  	ts, err := rawConfig.(Buildable).Build()
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	return &core.OutboundHandlerConfig{
   357  		SenderSettings: serial.ToTypedMessage(senderSettings),
   358  		Tag:            c.Tag,
   359  		ProxySettings:  serial.ToTypedMessage(ts),
   360  	}, nil
   361  }
   362  
   363  type StatsConfig struct{}
   364  
   365  // Build implements Buildable.
   366  func (c *StatsConfig) Build() (*stats.Config, error) {
   367  	return &stats.Config{}, nil
   368  }
   369  
   370  type Config struct {
   371  	// Port of this Point server.
   372  	// Deprecated: Port exists for historical compatibility
   373  	// and should not be used.
   374  	Port uint16 `json:"port"`
   375  
   376  	// Deprecated: InboundConfig exists for historical compatibility
   377  	// and should not be used.
   378  	InboundConfig *InboundDetourConfig `json:"inbound"`
   379  
   380  	// Deprecated: OutboundConfig exists for historical compatibility
   381  	// and should not be used.
   382  	OutboundConfig *OutboundDetourConfig `json:"outbound"`
   383  
   384  	// Deprecated: InboundDetours exists for historical compatibility
   385  	// and should not be used.
   386  	InboundDetours []InboundDetourConfig `json:"inboundDetour"`
   387  
   388  	// Deprecated: OutboundDetours exists for historical compatibility
   389  	// and should not be used.
   390  	OutboundDetours []OutboundDetourConfig `json:"outboundDetour"`
   391  
   392  	LogConfig       *LogConfig             `json:"log"`
   393  	RouterConfig    *RouterConfig          `json:"routing"`
   394  	DNSConfig       *DNSConfig             `json:"dns"`
   395  	InboundConfigs  []InboundDetourConfig  `json:"inbounds"`
   396  	OutboundConfigs []OutboundDetourConfig `json:"outbounds"`
   397  	Transport       *TransportConfig       `json:"transport"`
   398  	Policy          *PolicyConfig          `json:"policy"`
   399  	API             *APIConfig             `json:"api"`
   400  	Stats           *StatsConfig           `json:"stats"`
   401  	Reverse         *ReverseConfig         `json:"reverse"`
   402  	FakeDNS         *FakeDNSConfig         `json:"fakeDns"`
   403  }
   404  
   405  func (c *Config) findInboundTag(tag string) int {
   406  	found := -1
   407  	for idx, ib := range c.InboundConfigs {
   408  		if ib.Tag == tag {
   409  			found = idx
   410  			break
   411  		}
   412  	}
   413  	return found
   414  }
   415  
   416  func (c *Config) findOutboundTag(tag string) int {
   417  	found := -1
   418  	for idx, ob := range c.OutboundConfigs {
   419  		if ob.Tag == tag {
   420  			found = idx
   421  			break
   422  		}
   423  	}
   424  	return found
   425  }
   426  
   427  // Override method accepts another Config overrides the current attribute
   428  func (c *Config) Override(o *Config, fn string) {
   429  	// only process the non-deprecated members
   430  
   431  	if o.LogConfig != nil {
   432  		c.LogConfig = o.LogConfig
   433  	}
   434  	if o.RouterConfig != nil {
   435  		c.RouterConfig = o.RouterConfig
   436  	}
   437  	if o.DNSConfig != nil {
   438  		c.DNSConfig = o.DNSConfig
   439  	}
   440  	if o.Transport != nil {
   441  		c.Transport = o.Transport
   442  	}
   443  	if o.Policy != nil {
   444  		c.Policy = o.Policy
   445  	}
   446  	if o.API != nil {
   447  		c.API = o.API
   448  	}
   449  	if o.Stats != nil {
   450  		c.Stats = o.Stats
   451  	}
   452  	if o.Reverse != nil {
   453  		c.Reverse = o.Reverse
   454  	}
   455  
   456  	if o.FakeDNS != nil {
   457  		c.FakeDNS = o.FakeDNS
   458  	}
   459  
   460  	// deprecated attrs... keep them for now
   461  	if o.InboundConfig != nil {
   462  		c.InboundConfig = o.InboundConfig
   463  	}
   464  	if o.OutboundConfig != nil {
   465  		c.OutboundConfig = o.OutboundConfig
   466  	}
   467  	if o.InboundDetours != nil {
   468  		c.InboundDetours = o.InboundDetours
   469  	}
   470  	if o.OutboundDetours != nil {
   471  		c.OutboundDetours = o.OutboundDetours
   472  	}
   473  	// deprecated attrs
   474  
   475  	// update the Inbound in slice if the only one in overide config has same tag
   476  	if len(o.InboundConfigs) > 0 {
   477  		if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 {
   478  			if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 {
   479  				c.InboundConfigs[idx] = o.InboundConfigs[0]
   480  				ctllog.Println("[", fn, "] updated inbound with tag: ", o.InboundConfigs[0].Tag)
   481  			} else {
   482  				c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0])
   483  				ctllog.Println("[", fn, "] appended inbound with tag: ", o.InboundConfigs[0].Tag)
   484  			}
   485  		} else {
   486  			c.InboundConfigs = o.InboundConfigs
   487  		}
   488  	}
   489  
   490  	// update the Outbound in slice if the only one in overide config has same tag
   491  	if len(o.OutboundConfigs) > 0 {
   492  		if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 {
   493  			if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 {
   494  				c.OutboundConfigs[idx] = o.OutboundConfigs[0]
   495  				ctllog.Println("[", fn, "] updated outbound with tag: ", o.OutboundConfigs[0].Tag)
   496  			} else {
   497  				if strings.Contains(strings.ToLower(fn), "tail") {
   498  					c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0])
   499  					ctllog.Println("[", fn, "] appended outbound with tag: ", o.OutboundConfigs[0].Tag)
   500  				} else {
   501  					c.OutboundConfigs = append(o.OutboundConfigs, c.OutboundConfigs...)
   502  					ctllog.Println("[", fn, "] prepended outbound with tag: ", o.OutboundConfigs[0].Tag)
   503  				}
   504  			}
   505  		} else {
   506  			c.OutboundConfigs = o.OutboundConfigs
   507  		}
   508  	}
   509  }
   510  
   511  func applyTransportConfig(s *StreamConfig, t *TransportConfig) {
   512  	if s.TCPSettings == nil {
   513  		s.TCPSettings = t.TCPConfig
   514  	}
   515  	if s.KCPSettings == nil {
   516  		s.KCPSettings = t.KCPConfig
   517  	}
   518  	if s.WSSettings == nil {
   519  		s.WSSettings = t.WSConfig
   520  	}
   521  	if s.HTTPSettings == nil {
   522  		s.HTTPSettings = t.HTTPConfig
   523  	}
   524  	if s.DSSettings == nil {
   525  		s.DSSettings = t.DSConfig
   526  	}
   527  }
   528  
   529  // Build implements Buildable.
   530  func (c *Config) Build() (*core.Config, error) {
   531  	if err := PostProcessConfigureFile(c); err != nil {
   532  		return nil, err
   533  	}
   534  
   535  	config := &core.Config{
   536  		App: []*serial.TypedMessage{
   537  			serial.ToTypedMessage(&dispatcher.Config{}),
   538  			serial.ToTypedMessage(&proxyman.InboundConfig{}),
   539  			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
   540  		},
   541  	}
   542  
   543  	if c.API != nil {
   544  		apiConf, err := c.API.Build()
   545  		if err != nil {
   546  			return nil, err
   547  		}
   548  		config.App = append(config.App, serial.ToTypedMessage(apiConf))
   549  	}
   550  
   551  	if c.Stats != nil {
   552  		statsConf, err := c.Stats.Build()
   553  		if err != nil {
   554  			return nil, err
   555  		}
   556  		config.App = append(config.App, serial.ToTypedMessage(statsConf))
   557  	}
   558  
   559  	var logConfMsg *serial.TypedMessage
   560  	if c.LogConfig != nil {
   561  		logConfMsg = serial.ToTypedMessage(c.LogConfig.Build())
   562  	} else {
   563  		logConfMsg = serial.ToTypedMessage(DefaultLogConfig())
   564  	}
   565  	// let logger module be the first App to start,
   566  	// so that other modules could print log during initiating
   567  	config.App = append([]*serial.TypedMessage{logConfMsg}, config.App...)
   568  
   569  	if c.RouterConfig != nil {
   570  		routerConfig, err := c.RouterConfig.Build()
   571  		if err != nil {
   572  			return nil, err
   573  		}
   574  		config.App = append(config.App, serial.ToTypedMessage(routerConfig))
   575  	}
   576  
   577  	if c.DNSConfig != nil {
   578  		dnsApp, err := c.DNSConfig.Build()
   579  		if err != nil {
   580  			return nil, newError("failed to parse DNS config").Base(err)
   581  		}
   582  		config.App = append(config.App, serial.ToTypedMessage(dnsApp))
   583  	}
   584  
   585  	if c.Policy != nil {
   586  		pc, err := c.Policy.Build()
   587  		if err != nil {
   588  			return nil, err
   589  		}
   590  		config.App = append(config.App, serial.ToTypedMessage(pc))
   591  	}
   592  
   593  	if c.Reverse != nil {
   594  		r, err := c.Reverse.Build()
   595  		if err != nil {
   596  			return nil, err
   597  		}
   598  		config.App = append(config.App, serial.ToTypedMessage(r))
   599  	}
   600  
   601  	if c.FakeDNS != nil {
   602  		r, err := c.FakeDNS.Build()
   603  		if err != nil {
   604  			return nil, err
   605  		}
   606  		config.App = append(config.App, serial.ToTypedMessage(r))
   607  	}
   608  
   609  	var inbounds []InboundDetourConfig
   610  
   611  	if c.InboundConfig != nil {
   612  		inbounds = append(inbounds, *c.InboundConfig)
   613  	}
   614  
   615  	if len(c.InboundDetours) > 0 {
   616  		inbounds = append(inbounds, c.InboundDetours...)
   617  	}
   618  
   619  	if len(c.InboundConfigs) > 0 {
   620  		inbounds = append(inbounds, c.InboundConfigs...)
   621  	}
   622  
   623  	// Backward compatibility.
   624  	if len(inbounds) > 0 && inbounds[0].PortRange == nil && c.Port > 0 {
   625  		inbounds[0].PortRange = &PortRange{
   626  			From: uint32(c.Port),
   627  			To:   uint32(c.Port),
   628  		}
   629  	}
   630  
   631  	for _, rawInboundConfig := range inbounds {
   632  		if c.Transport != nil {
   633  			if rawInboundConfig.StreamSetting == nil {
   634  				rawInboundConfig.StreamSetting = &StreamConfig{}
   635  			}
   636  			applyTransportConfig(rawInboundConfig.StreamSetting, c.Transport)
   637  		}
   638  		ic, err := rawInboundConfig.Build()
   639  		if err != nil {
   640  			return nil, err
   641  		}
   642  		config.Inbound = append(config.Inbound, ic)
   643  	}
   644  
   645  	var outbounds []OutboundDetourConfig
   646  
   647  	if c.OutboundConfig != nil {
   648  		outbounds = append(outbounds, *c.OutboundConfig)
   649  	}
   650  
   651  	if len(c.OutboundDetours) > 0 {
   652  		outbounds = append(outbounds, c.OutboundDetours...)
   653  	}
   654  
   655  	if len(c.OutboundConfigs) > 0 {
   656  		outbounds = append(outbounds, c.OutboundConfigs...)
   657  	}
   658  
   659  	for _, rawOutboundConfig := range outbounds {
   660  		if c.Transport != nil {
   661  			if rawOutboundConfig.StreamSetting == nil {
   662  				rawOutboundConfig.StreamSetting = &StreamConfig{}
   663  			}
   664  			applyTransportConfig(rawOutboundConfig.StreamSetting, c.Transport)
   665  		}
   666  		oc, err := rawOutboundConfig.Build()
   667  		if err != nil {
   668  			return nil, err
   669  		}
   670  		config.Outbound = append(config.Outbound, oc)
   671  	}
   672  
   673  	return config, nil
   674  }