1package conf
2
3import (
4	"encoding/json"
5	"math"
6	"net/url"
7	"strconv"
8	"strings"
9
10	"github.com/golang/protobuf/proto"
11
12	"github.com/xtls/xray-core/common/platform/filesystem"
13	"github.com/xtls/xray-core/common/protocol"
14	"github.com/xtls/xray-core/common/serial"
15	"github.com/xtls/xray-core/transport/internet"
16	"github.com/xtls/xray-core/transport/internet/domainsocket"
17	httpheader "github.com/xtls/xray-core/transport/internet/headers/http"
18	"github.com/xtls/xray-core/transport/internet/http"
19	"github.com/xtls/xray-core/transport/internet/kcp"
20	"github.com/xtls/xray-core/transport/internet/quic"
21	"github.com/xtls/xray-core/transport/internet/tcp"
22	"github.com/xtls/xray-core/transport/internet/tls"
23	"github.com/xtls/xray-core/transport/internet/websocket"
24	"github.com/xtls/xray-core/transport/internet/xtls"
25)
26
27var (
28	kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
29		"none":         func() interface{} { return new(NoOpAuthenticator) },
30		"srtp":         func() interface{} { return new(SRTPAuthenticator) },
31		"utp":          func() interface{} { return new(UTPAuthenticator) },
32		"wechat-video": func() interface{} { return new(WechatVideoAuthenticator) },
33		"dtls":         func() interface{} { return new(DTLSAuthenticator) },
34		"wireguard":    func() interface{} { return new(WireguardAuthenticator) },
35	}, "type", "")
36
37	tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
38		"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
39		"http": func() interface{} { return new(Authenticator) },
40	}, "type", "")
41)
42
43type KCPConfig struct {
44	Mtu             *uint32         `json:"mtu"`
45	Tti             *uint32         `json:"tti"`
46	UpCap           *uint32         `json:"uplinkCapacity"`
47	DownCap         *uint32         `json:"downlinkCapacity"`
48	Congestion      *bool           `json:"congestion"`
49	ReadBufferSize  *uint32         `json:"readBufferSize"`
50	WriteBufferSize *uint32         `json:"writeBufferSize"`
51	HeaderConfig    json.RawMessage `json:"header"`
52	Seed            *string         `json:"seed"`
53}
54
55// Build implements Buildable.
56func (c *KCPConfig) Build() (proto.Message, error) {
57	config := new(kcp.Config)
58
59	if c.Mtu != nil {
60		mtu := *c.Mtu
61		if mtu < 576 || mtu > 1460 {
62			return nil, newError("invalid mKCP MTU size: ", mtu).AtError()
63		}
64		config.Mtu = &kcp.MTU{Value: mtu}
65	}
66	if c.Tti != nil {
67		tti := *c.Tti
68		if tti < 10 || tti > 100 {
69			return nil, newError("invalid mKCP TTI: ", tti).AtError()
70		}
71		config.Tti = &kcp.TTI{Value: tti}
72	}
73	if c.UpCap != nil {
74		config.UplinkCapacity = &kcp.UplinkCapacity{Value: *c.UpCap}
75	}
76	if c.DownCap != nil {
77		config.DownlinkCapacity = &kcp.DownlinkCapacity{Value: *c.DownCap}
78	}
79	if c.Congestion != nil {
80		config.Congestion = *c.Congestion
81	}
82	if c.ReadBufferSize != nil {
83		size := *c.ReadBufferSize
84		if size > 0 {
85			config.ReadBuffer = &kcp.ReadBuffer{Size: size * 1024 * 1024}
86		} else {
87			config.ReadBuffer = &kcp.ReadBuffer{Size: 512 * 1024}
88		}
89	}
90	if c.WriteBufferSize != nil {
91		size := *c.WriteBufferSize
92		if size > 0 {
93			config.WriteBuffer = &kcp.WriteBuffer{Size: size * 1024 * 1024}
94		} else {
95			config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024}
96		}
97	}
98	if len(c.HeaderConfig) > 0 {
99		headerConfig, _, err := kcpHeaderLoader.Load(c.HeaderConfig)
100		if err != nil {
101			return nil, newError("invalid mKCP header config.").Base(err).AtError()
102		}
103		ts, err := headerConfig.(Buildable).Build()
104		if err != nil {
105			return nil, newError("invalid mKCP header config").Base(err).AtError()
106		}
107		config.HeaderConfig = serial.ToTypedMessage(ts)
108	}
109
110	if c.Seed != nil {
111		config.Seed = &kcp.EncryptionSeed{Seed: *c.Seed}
112	}
113
114	return config, nil
115}
116
117type TCPConfig struct {
118	HeaderConfig        json.RawMessage `json:"header"`
119	AcceptProxyProtocol bool            `json:"acceptProxyProtocol"`
120}
121
122// Build implements Buildable.
123func (c *TCPConfig) Build() (proto.Message, error) {
124	config := new(tcp.Config)
125	if len(c.HeaderConfig) > 0 {
126		headerConfig, _, err := tcpHeaderLoader.Load(c.HeaderConfig)
127		if err != nil {
128			return nil, newError("invalid TCP header config").Base(err).AtError()
129		}
130		ts, err := headerConfig.(Buildable).Build()
131		if err != nil {
132			return nil, newError("invalid TCP header config").Base(err).AtError()
133		}
134		config.HeaderSettings = serial.ToTypedMessage(ts)
135	}
136	if c.AcceptProxyProtocol {
137		config.AcceptProxyProtocol = c.AcceptProxyProtocol
138	}
139	return config, nil
140}
141
142type WebSocketConfig struct {
143	Path                string            `json:"path"`
144	Path2               string            `json:"Path"` // The key was misspelled. For backward compatibility, we have to keep track the old key.
145	Headers             map[string]string `json:"headers"`
146	AcceptProxyProtocol bool              `json:"acceptProxyProtocol"`
147}
148
149// Build implements Buildable.
150func (c *WebSocketConfig) Build() (proto.Message, error) {
151	path := c.Path
152	if path == "" && c.Path2 != "" {
153		path = c.Path2
154	}
155	header := make([]*websocket.Header, 0, 32)
156	for key, value := range c.Headers {
157		header = append(header, &websocket.Header{
158			Key:   key,
159			Value: value,
160		})
161	}
162	var ed uint32
163	if u, err := url.Parse(path); err == nil {
164		if q := u.Query(); q.Get("ed") != "" {
165			Ed, _ := strconv.Atoi(q.Get("ed"))
166			ed = uint32(Ed)
167			q.Del("ed")
168			u.RawQuery = q.Encode()
169			path = u.String()
170		}
171	}
172	config := &websocket.Config{
173		Path:   path,
174		Header: header,
175		Ed:     ed,
176	}
177	if c.AcceptProxyProtocol {
178		config.AcceptProxyProtocol = c.AcceptProxyProtocol
179	}
180	return config, nil
181}
182
183type HTTPConfig struct {
184	Host               *StringList            `json:"host"`
185	Path               string                 `json:"path"`
186	ReadIdleTimeout    int32                  `json:"read_idle_timeout"`
187	HealthCheckTimeout int32                  `json:"health_check_timeout"`
188	Method             string                 `json:"method"`
189	Headers            map[string]*StringList `json:"headers"`
190}
191
192// Build implements Buildable.
193func (c *HTTPConfig) Build() (proto.Message, error) {
194	if c.ReadIdleTimeout <= 0 {
195		c.ReadIdleTimeout = 0
196	}
197	if c.HealthCheckTimeout <= 0 {
198		c.HealthCheckTimeout = 0
199	}
200	config := &http.Config{
201		Path:               c.Path,
202		IdleTimeout:        c.ReadIdleTimeout,
203		HealthCheckTimeout: c.HealthCheckTimeout,
204	}
205	if c.Host != nil {
206		config.Host = []string(*c.Host)
207	}
208	if c.Method != "" {
209		config.Method = c.Method
210	}
211	if len(c.Headers) > 0 {
212		config.Header = make([]*httpheader.Header, 0, len(c.Headers))
213		headerNames := sortMapKeys(c.Headers)
214		for _, key := range headerNames {
215			value := c.Headers[key]
216			if value == nil {
217				return nil, newError("empty HTTP header value: " + key).AtError()
218			}
219			config.Header = append(config.Header, &httpheader.Header{
220				Name:  key,
221				Value: append([]string(nil), (*value)...),
222			})
223		}
224	}
225	return config, nil
226}
227
228type QUICConfig struct {
229	Header   json.RawMessage `json:"header"`
230	Security string          `json:"security"`
231	Key      string          `json:"key"`
232}
233
234// Build implements Buildable.
235func (c *QUICConfig) Build() (proto.Message, error) {
236	config := &quic.Config{
237		Key: c.Key,
238	}
239
240	if len(c.Header) > 0 {
241		headerConfig, _, err := kcpHeaderLoader.Load(c.Header)
242		if err != nil {
243			return nil, newError("invalid QUIC header config.").Base(err).AtError()
244		}
245		ts, err := headerConfig.(Buildable).Build()
246		if err != nil {
247			return nil, newError("invalid QUIC header config").Base(err).AtError()
248		}
249		config.Header = serial.ToTypedMessage(ts)
250	}
251
252	var st protocol.SecurityType
253	switch strings.ToLower(c.Security) {
254	case "aes-128-gcm":
255		st = protocol.SecurityType_AES128_GCM
256	case "chacha20-poly1305":
257		st = protocol.SecurityType_CHACHA20_POLY1305
258	default:
259		st = protocol.SecurityType_NONE
260	}
261
262	config.Security = &protocol.SecurityConfig{
263		Type: st,
264	}
265
266	return config, nil
267}
268
269type DomainSocketConfig struct {
270	Path     string `json:"path"`
271	Abstract bool   `json:"abstract"`
272	Padding  bool   `json:"padding"`
273}
274
275// Build implements Buildable.
276func (c *DomainSocketConfig) Build() (proto.Message, error) {
277	return &domainsocket.Config{
278		Path:     c.Path,
279		Abstract: c.Abstract,
280		Padding:  c.Padding,
281	}, nil
282}
283
284func readFileOrString(f string, s []string) ([]byte, error) {
285	if len(f) > 0 {
286		return filesystem.ReadFile(f)
287	}
288	if len(s) > 0 {
289		return []byte(strings.Join(s, "\n")), nil
290	}
291	return nil, newError("both file and bytes are empty.")
292}
293
294type TLSCertConfig struct {
295	CertFile       string   `json:"certificateFile"`
296	CertStr        []string `json:"certificate"`
297	KeyFile        string   `json:"keyFile"`
298	KeyStr         []string `json:"key"`
299	Usage          string   `json:"usage"`
300	OcspStapling   uint64   `json:"ocspStapling"`
301	OneTimeLoading bool     `json:"oneTimeLoading"`
302}
303
304// Build implements Buildable.
305func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
306	certificate := new(tls.Certificate)
307
308	cert, err := readFileOrString(c.CertFile, c.CertStr)
309	if err != nil {
310		return nil, newError("failed to parse certificate").Base(err)
311	}
312	certificate.Certificate = cert
313	certificate.CertificatePath = c.CertFile
314
315	if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
316		key, err := readFileOrString(c.KeyFile, c.KeyStr)
317		if err != nil {
318			return nil, newError("failed to parse key").Base(err)
319		}
320		certificate.Key = key
321		certificate.KeyPath = c.KeyFile
322	}
323
324	switch strings.ToLower(c.Usage) {
325	case "encipherment":
326		certificate.Usage = tls.Certificate_ENCIPHERMENT
327	case "verify":
328		certificate.Usage = tls.Certificate_AUTHORITY_VERIFY
329	case "issue":
330		certificate.Usage = tls.Certificate_AUTHORITY_ISSUE
331	default:
332		certificate.Usage = tls.Certificate_ENCIPHERMENT
333	}
334	if certificate.KeyPath == "" && certificate.CertificatePath == "" {
335		certificate.OneTimeLoading = true
336	} else {
337		certificate.OneTimeLoading = c.OneTimeLoading
338	}
339	certificate.OcspStapling = c.OcspStapling
340
341	return certificate, nil
342}
343
344type TLSConfig struct {
345	Insecure                 bool             `json:"allowInsecure"`
346	Certs                    []*TLSCertConfig `json:"certificates"`
347	ServerName               string           `json:"serverName"`
348	ALPN                     *StringList      `json:"alpn"`
349	EnableSessionResumption  bool             `json:"enableSessionResumption"`
350	DisableSystemRoot        bool             `json:"disableSystemRoot"`
351	MinVersion               string           `json:"minVersion"`
352	MaxVersion               string           `json:"maxVersion"`
353	CipherSuites             string           `json:"cipherSuites"`
354	PreferServerCipherSuites bool             `json:"preferServerCipherSuites"`
355	Fingerprint              string           `json:"fingerprint"`
356	RejectUnknownSNI         bool             `json:"rejectUnknownSni"`
357}
358
359// Build implements Buildable.
360func (c *TLSConfig) Build() (proto.Message, error) {
361	config := new(tls.Config)
362	config.Certificate = make([]*tls.Certificate, len(c.Certs))
363	for idx, certConf := range c.Certs {
364		cert, err := certConf.Build()
365		if err != nil {
366			return nil, err
367		}
368		config.Certificate[idx] = cert
369	}
370	serverName := c.ServerName
371	config.AllowInsecure = c.Insecure
372	if len(c.ServerName) > 0 {
373		config.ServerName = serverName
374	}
375	if c.ALPN != nil && len(*c.ALPN) > 0 {
376		config.NextProtocol = []string(*c.ALPN)
377	}
378	config.EnableSessionResumption = c.EnableSessionResumption
379	config.DisableSystemRoot = c.DisableSystemRoot
380	config.MinVersion = c.MinVersion
381	config.MaxVersion = c.MaxVersion
382	config.CipherSuites = c.CipherSuites
383	config.PreferServerCipherSuites = c.PreferServerCipherSuites
384	config.Fingerprint = strings.ToLower(c.Fingerprint)
385	config.RejectUnknownSni = c.RejectUnknownSNI
386	return config, nil
387}
388
389type XTLSCertConfig struct {
390	CertFile       string   `json:"certificateFile"`
391	CertStr        []string `json:"certificate"`
392	KeyFile        string   `json:"keyFile"`
393	KeyStr         []string `json:"key"`
394	Usage          string   `json:"usage"`
395	OcspStapling   uint64   `json:"ocspStapling"`
396	OneTimeLoading bool     `json:"oneTimeLoading"`
397}
398
399// Build implements Buildable.
400func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
401	certificate := new(xtls.Certificate)
402	cert, err := readFileOrString(c.CertFile, c.CertStr)
403	if err != nil {
404		return nil, newError("failed to parse certificate").Base(err)
405	}
406	certificate.Certificate = cert
407	certificate.CertificatePath = c.CertFile
408
409	if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
410		key, err := readFileOrString(c.KeyFile, c.KeyStr)
411		if err != nil {
412			return nil, newError("failed to parse key").Base(err)
413		}
414		certificate.Key = key
415		certificate.KeyPath = c.KeyFile
416	}
417
418	switch strings.ToLower(c.Usage) {
419	case "encipherment":
420		certificate.Usage = xtls.Certificate_ENCIPHERMENT
421	case "verify":
422		certificate.Usage = xtls.Certificate_AUTHORITY_VERIFY
423	case "issue":
424		certificate.Usage = xtls.Certificate_AUTHORITY_ISSUE
425	default:
426		certificate.Usage = xtls.Certificate_ENCIPHERMENT
427	}
428	if certificate.KeyPath == "" && certificate.CertificatePath == "" {
429		certificate.OneTimeLoading = true
430	} else {
431		certificate.OneTimeLoading = c.OneTimeLoading
432	}
433	certificate.OcspStapling = c.OcspStapling
434
435	return certificate, nil
436}
437
438type XTLSConfig struct {
439	Insecure                 bool              `json:"allowInsecure"`
440	Certs                    []*XTLSCertConfig `json:"certificates"`
441	ServerName               string            `json:"serverName"`
442	ALPN                     *StringList       `json:"alpn"`
443	EnableSessionResumption  bool              `json:"enableSessionResumption"`
444	DisableSystemRoot        bool              `json:"disableSystemRoot"`
445	MinVersion               string            `json:"minVersion"`
446	MaxVersion               string            `json:"maxVersion"`
447	CipherSuites             string            `json:"cipherSuites"`
448	PreferServerCipherSuites bool              `json:"preferServerCipherSuites"`
449	RejectUnknownSNI         bool              `json:"rejectUnknownSni"`
450}
451
452// Build implements Buildable.
453func (c *XTLSConfig) Build() (proto.Message, error) {
454	config := new(xtls.Config)
455	config.Certificate = make([]*xtls.Certificate, len(c.Certs))
456	for idx, certConf := range c.Certs {
457		cert, err := certConf.Build()
458		if err != nil {
459			return nil, err
460		}
461		config.Certificate[idx] = cert
462	}
463	serverName := c.ServerName
464	config.AllowInsecure = c.Insecure
465	if len(c.ServerName) > 0 {
466		config.ServerName = serverName
467	}
468	if c.ALPN != nil && len(*c.ALPN) > 0 {
469		config.NextProtocol = []string(*c.ALPN)
470	}
471	config.EnableSessionResumption = c.EnableSessionResumption
472	config.DisableSystemRoot = c.DisableSystemRoot
473	config.MinVersion = c.MinVersion
474	config.MaxVersion = c.MaxVersion
475	config.CipherSuites = c.CipherSuites
476	config.PreferServerCipherSuites = c.PreferServerCipherSuites
477	config.RejectUnknownSni = c.RejectUnknownSNI
478	return config, nil
479}
480
481type TransportProtocol string
482
483// Build implements Buildable.
484func (p TransportProtocol) Build() (string, error) {
485	switch strings.ToLower(string(p)) {
486	case "tcp":
487		return "tcp", nil
488	case "kcp", "mkcp":
489		return "mkcp", nil
490	case "ws", "websocket":
491		return "websocket", nil
492	case "h2", "http":
493		return "http", nil
494	case "ds", "domainsocket":
495		return "domainsocket", nil
496	case "quic":
497		return "quic", nil
498	case "grpc", "gun":
499		return "grpc", nil
500	default:
501		return "", newError("Config: unknown transport protocol: ", p)
502	}
503}
504
505type SocketConfig struct {
506	Mark                int32       `json:"mark"`
507	TFO                 interface{} `json:"tcpFastOpen"`
508	TProxy              string      `json:"tproxy"`
509	AcceptProxyProtocol bool        `json:"acceptProxyProtocol"`
510	DomainStrategy      string      `json:"domainStrategy"`
511	DialerProxy         string      `json:"dialerProxy"`
512
513	TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
514}
515
516// Build implements Buildable.
517func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
518	tfo := int32(0) // don't invoke setsockopt() for TFO
519	if c.TFO != nil {
520		switch v := c.TFO.(type) {
521		case bool:
522			if v {
523				tfo = 256
524			} else {
525				tfo = -1 // TFO need to be disabled
526			}
527		case float64:
528			tfo = int32(math.Min(v, math.MaxInt32))
529		default:
530			return nil, newError("tcpFastOpen: only boolean and integer value is acceptable")
531		}
532	}
533	var tproxy internet.SocketConfig_TProxyMode
534	switch strings.ToLower(c.TProxy) {
535	case "tproxy":
536		tproxy = internet.SocketConfig_TProxy
537	case "redirect":
538		tproxy = internet.SocketConfig_Redirect
539	default:
540		tproxy = internet.SocketConfig_Off
541	}
542
543	dStrategy := internet.DomainStrategy_AS_IS
544	switch strings.ToLower(c.DomainStrategy) {
545	case "useip", "use_ip":
546		dStrategy = internet.DomainStrategy_USE_IP
547	case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4":
548		dStrategy = internet.DomainStrategy_USE_IP4
549	case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6":
550		dStrategy = internet.DomainStrategy_USE_IP6
551	}
552
553	return &internet.SocketConfig{
554		Mark:                 c.Mark,
555		Tfo:                  tfo,
556		Tproxy:               tproxy,
557		DomainStrategy:       dStrategy,
558		AcceptProxyProtocol:  c.AcceptProxyProtocol,
559		DialerProxy:          c.DialerProxy,
560		TcpKeepAliveInterval: c.TCPKeepAliveInterval,
561	}, nil
562}
563
564type StreamConfig struct {
565	Network        *TransportProtocol  `json:"network"`
566	Security       string              `json:"security"`
567	TLSSettings    *TLSConfig          `json:"tlsSettings"`
568	XTLSSettings   *XTLSConfig         `json:"xtlsSettings"`
569	TCPSettings    *TCPConfig          `json:"tcpSettings"`
570	KCPSettings    *KCPConfig          `json:"kcpSettings"`
571	WSSettings     *WebSocketConfig    `json:"wsSettings"`
572	HTTPSettings   *HTTPConfig         `json:"httpSettings"`
573	DSSettings     *DomainSocketConfig `json:"dsSettings"`
574	QUICSettings   *QUICConfig         `json:"quicSettings"`
575	SocketSettings *SocketConfig       `json:"sockopt"`
576	GRPCConfig     *GRPCConfig         `json:"grpcSettings"`
577	GUNConfig      *GRPCConfig         `json:"gunSettings"`
578}
579
580// Build implements Buildable.
581func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
582	config := &internet.StreamConfig{
583		ProtocolName: "tcp",
584	}
585	if c.Network != nil {
586		protocol, err := c.Network.Build()
587		if err != nil {
588			return nil, err
589		}
590		config.ProtocolName = protocol
591	}
592	if strings.EqualFold(c.Security, "tls") {
593		tlsSettings := c.TLSSettings
594		if tlsSettings == nil {
595			if c.XTLSSettings != nil {
596				return nil, newError(`TLS: Please use "tlsSettings" instead of "xtlsSettings".`)
597			}
598			tlsSettings = &TLSConfig{}
599		}
600		ts, err := tlsSettings.Build()
601		if err != nil {
602			return nil, newError("Failed to build TLS config.").Base(err)
603		}
604		tm := serial.ToTypedMessage(ts)
605		config.SecuritySettings = append(config.SecuritySettings, tm)
606		config.SecurityType = tm.Type
607	}
608	if strings.EqualFold(c.Security, "xtls") {
609		if config.ProtocolName != "tcp" && config.ProtocolName != "mkcp" && config.ProtocolName != "domainsocket" {
610			return nil, newError("XTLS only supports TCP, mKCP and DomainSocket for now.")
611		}
612		xtlsSettings := c.XTLSSettings
613		if xtlsSettings == nil {
614			if c.TLSSettings != nil {
615				return nil, newError(`XTLS: Please use "xtlsSettings" instead of "tlsSettings".`)
616			}
617			xtlsSettings = &XTLSConfig{}
618		}
619		ts, err := xtlsSettings.Build()
620		if err != nil {
621			return nil, newError("Failed to build XTLS config.").Base(err)
622		}
623		tm := serial.ToTypedMessage(ts)
624		config.SecuritySettings = append(config.SecuritySettings, tm)
625		config.SecurityType = tm.Type
626	}
627	if c.TCPSettings != nil {
628		ts, err := c.TCPSettings.Build()
629		if err != nil {
630			return nil, newError("Failed to build TCP config.").Base(err)
631		}
632		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
633			ProtocolName: "tcp",
634			Settings:     serial.ToTypedMessage(ts),
635		})
636	}
637	if c.KCPSettings != nil {
638		ts, err := c.KCPSettings.Build()
639		if err != nil {
640			return nil, newError("Failed to build mKCP config.").Base(err)
641		}
642		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
643			ProtocolName: "mkcp",
644			Settings:     serial.ToTypedMessage(ts),
645		})
646	}
647	if c.WSSettings != nil {
648		ts, err := c.WSSettings.Build()
649		if err != nil {
650			return nil, newError("Failed to build WebSocket config.").Base(err)
651		}
652		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
653			ProtocolName: "websocket",
654			Settings:     serial.ToTypedMessage(ts),
655		})
656	}
657	if c.HTTPSettings != nil {
658		ts, err := c.HTTPSettings.Build()
659		if err != nil {
660			return nil, newError("Failed to build HTTP config.").Base(err)
661		}
662		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
663			ProtocolName: "http",
664			Settings:     serial.ToTypedMessage(ts),
665		})
666	}
667	if c.DSSettings != nil {
668		ds, err := c.DSSettings.Build()
669		if err != nil {
670			return nil, newError("Failed to build DomainSocket config.").Base(err)
671		}
672		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
673			ProtocolName: "domainsocket",
674			Settings:     serial.ToTypedMessage(ds),
675		})
676	}
677	if c.QUICSettings != nil {
678		qs, err := c.QUICSettings.Build()
679		if err != nil {
680			return nil, newError("Failed to build QUIC config").Base(err)
681		}
682		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
683			ProtocolName: "quic",
684			Settings:     serial.ToTypedMessage(qs),
685		})
686	}
687	if c.GRPCConfig == nil {
688		c.GRPCConfig = c.GUNConfig
689	}
690	if c.GRPCConfig != nil {
691		gs, err := c.GRPCConfig.Build()
692		if err != nil {
693			return nil, newError("Failed to build gRPC config.").Base(err)
694		}
695		config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
696			ProtocolName: "grpc",
697			Settings:     serial.ToTypedMessage(gs),
698		})
699	}
700	if c.SocketSettings != nil {
701		ss, err := c.SocketSettings.Build()
702		if err != nil {
703			return nil, newError("Failed to build sockopt").Base(err)
704		}
705		config.SocketSettings = ss
706	}
707	return config, nil
708}
709
710type ProxyConfig struct {
711	Tag string `json:"tag"`
712
713	// TransportLayerProxy: For compatibility.
714	TransportLayerProxy bool `json:"transportLayer"`
715}
716
717// Build implements Buildable.
718func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
719	if v.Tag == "" {
720		return nil, newError("Proxy tag is not set.")
721	}
722	return &internet.ProxyConfig{
723		Tag:                 v.Tag,
724		TransportLayerProxy: v.TransportLayerProxy,
725	}, nil
726}
727