github.com/moqsien/xraycore@v1.8.5/infra/conf/transport_internet.go (about)

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