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