github.com/xraypb/Xray-core@v1.8.1/infra/conf/xray.go (about)

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