github.com/v2fly/v2ray-core/v4@v4.45.2/infra/conf/v2ray.go (about)

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