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