package conf import ( "encoding/json" "math" "strings" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/transport/internet" ) type CustomSockoptConfig struct { System string `json:"system"` Network string `json:"network"` Level string `json:"level"` Opt string `json:"opt"` Value string `json:"value"` Type string `json:"type"` } type HappyEyeballsConfig struct { PrioritizeIPv6 bool `json:"prioritizeIPv6"` TryDelayMs uint64 `json:"tryDelayMs"` Interleave uint32 `json:"interleave"` MaxConcurrentTry uint32 `json:"maxConcurrentTry"` } func (h *HappyEyeballsConfig) UnmarshalJSON(data []byte) error { innerHappyEyeballsConfig := struct { PrioritizeIPv6 bool `json:"prioritizeIPv6"` TryDelayMs uint64 `json:"tryDelayMs"` Interleave uint32 `json:"interleave"` MaxConcurrentTry uint32 `json:"maxConcurrentTry"` }{PrioritizeIPv6: false, Interleave: 1, TryDelayMs: 0, MaxConcurrentTry: 4} if err := json.Unmarshal(data, &innerHappyEyeballsConfig); err != nil { return err } h.PrioritizeIPv6 = innerHappyEyeballsConfig.PrioritizeIPv6 h.TryDelayMs = innerHappyEyeballsConfig.TryDelayMs h.Interleave = innerHappyEyeballsConfig.Interleave h.MaxConcurrentTry = innerHappyEyeballsConfig.MaxConcurrentTry return nil } type SocketConfig struct { Mark int32 `json:"mark"` TFO interface{} `json:"tcpFastOpen"` TProxy string `json:"tproxy"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` DomainStrategy string `json:"domainStrategy"` DialerProxy string `json:"dialerProxy"` TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"` TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"` TCPCongestion string `json:"tcpCongestion"` TCPWindowClamp int32 `json:"tcpWindowClamp"` TCPMaxSeg int32 `json:"tcpMaxSeg"` Penetrate bool `json:"penetrate"` TCPUserTimeout int32 `json:"tcpUserTimeout"` V6only bool `json:"v6only"` Interface string `json:"interface"` TcpMptcp bool `json:"tcpMptcp"` CustomSockopt []*CustomSockoptConfig `json:"customSockopt"` AddressPortStrategy string `json:"addressPortStrategy"` HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"` TrustedXForwardedFor []string `json:"trustedXForwardedFor"` } // Build implements Buildable. func (c *SocketConfig) Build() (*internet.SocketConfig, error) { tfo := int32(0) // don't invoke setsockopt() for TFO if c.TFO != nil { switch v := c.TFO.(type) { case bool: if v { tfo = 256 } else { tfo = -1 // TFO need to be disabled } case float64: tfo = int32(math.Min(v, math.MaxInt32)) default: return nil, errors.New("tcpFastOpen: only boolean and integer value is acceptable") } } var tproxy internet.SocketConfig_TProxyMode switch strings.ToLower(c.TProxy) { case "tproxy": tproxy = internet.SocketConfig_TProxy case "redirect": tproxy = internet.SocketConfig_Redirect default: tproxy = internet.SocketConfig_Off } dStrategy := internet.DomainStrategy_AS_IS switch strings.ToLower(c.DomainStrategy) { case "asis", "": dStrategy = internet.DomainStrategy_AS_IS case "useip": dStrategy = internet.DomainStrategy_USE_IP case "useipv4": dStrategy = internet.DomainStrategy_USE_IP4 case "useipv6": dStrategy = internet.DomainStrategy_USE_IP6 case "useipv4v6": dStrategy = internet.DomainStrategy_USE_IP46 case "useipv6v4": dStrategy = internet.DomainStrategy_USE_IP64 case "forceip": dStrategy = internet.DomainStrategy_FORCE_IP case "forceipv4": dStrategy = internet.DomainStrategy_FORCE_IP4 case "forceipv6": dStrategy = internet.DomainStrategy_FORCE_IP6 case "forceipv4v6": dStrategy = internet.DomainStrategy_FORCE_IP46 case "forceipv6v4": dStrategy = internet.DomainStrategy_FORCE_IP64 default: return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) } var customSockopts []*internet.CustomSockopt for _, copt := range c.CustomSockopt { customSockopt := &internet.CustomSockopt{ System: copt.System, Network: copt.Network, Level: copt.Level, Opt: copt.Opt, Value: copt.Value, Type: copt.Type, } customSockopts = append(customSockopts, customSockopt) } addressPortStrategy := internet.AddressPortStrategy_None switch strings.ToLower(c.AddressPortStrategy) { case "none", "": addressPortStrategy = internet.AddressPortStrategy_None case "srvportonly": addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly case "srvaddressonly": addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly case "srvportandaddress": addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress case "txtportonly": addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly case "txtaddressonly": addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly case "txtportandaddress": addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress default: return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy) } happyEyeballs := &internet.HappyEyeballsConfig{Interleave: 1, PrioritizeIpv6: false, TryDelayMs: 0, MaxConcurrentTry: 4} if c.HappyEyeballsSettings != nil { happyEyeballs.PrioritizeIpv6 = c.HappyEyeballsSettings.PrioritizeIPv6 happyEyeballs.Interleave = c.HappyEyeballsSettings.Interleave happyEyeballs.TryDelayMs = c.HappyEyeballsSettings.TryDelayMs happyEyeballs.MaxConcurrentTry = c.HappyEyeballsSettings.MaxConcurrentTry } return &internet.SocketConfig{ Mark: c.Mark, Tfo: tfo, Tproxy: tproxy, DomainStrategy: dStrategy, AcceptProxyProtocol: c.AcceptProxyProtocol, DialerProxy: c.DialerProxy, TcpKeepAliveInterval: c.TCPKeepAliveInterval, TcpKeepAliveIdle: c.TCPKeepAliveIdle, TcpCongestion: c.TCPCongestion, TcpWindowClamp: c.TCPWindowClamp, TcpMaxSeg: c.TCPMaxSeg, Penetrate: c.Penetrate, TcpUserTimeout: c.TCPUserTimeout, V6Only: c.V6only, Interface: c.Interface, TcpMptcp: c.TcpMptcp, CustomSockopt: customSockopts, AddressPortStrategy: addressPortStrategy, HappyEyeballs: happyEyeballs, TrustedXForwardedFor: c.TrustedXForwardedFor, }, nil }