package xray import ( "crypto/rand" "encoding/base64" "encoding/hex" "errors" "fmt" "time" "github.com/InazumaV/V2bX/api/panel" "github.com/InazumaV/V2bX/conf" "github.com/goccy/go-json" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/core" coreConf "github.com/xtls/xray-core/infra/conf" ) // BuildInbound build Inbound config for different protocol func buildInbound(option *conf.Options, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) { in := &coreConf.InboundDetourConfig{} var err error var network string switch nodeInfo.Type { case "vmess", "vless": err = buildV2ray(option, nodeInfo, in) network = nodeInfo.VAllss.Network case "trojan": err = buildTrojan(option, nodeInfo, in) if nodeInfo.Trojan.Network != "" { network = nodeInfo.Trojan.Network } else { network = "tcp" } case "shadowsocks": err = buildShadowsocks(option, nodeInfo, in) network = "tcp" default: return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.Type) } if err != nil { return nil, err } // Set network protocol // Set server port in.PortList = &coreConf.PortList{ Range: []coreConf.PortRange{ { From: uint32(nodeInfo.Common.ServerPort), To: uint32(nodeInfo.Common.ServerPort), }}, } // Set Listen IP address ipAddress := net.ParseAddress(option.ListenIP) in.ListenOn = &coreConf.Address{Address: ipAddress} // Set SniffingConfig sniffingConfig := &coreConf.SniffingConfig{ Enabled: true, DestOverride: &coreConf.StringList{"http", "tls"}, } if option.XrayOptions.DisableSniffing { sniffingConfig.Enabled = false } in.SniffingConfig = sniffingConfig switch network { case "tcp": if in.StreamSetting.TCPSettings != nil { in.StreamSetting.TCPSettings.AcceptProxyProtocol = option.XrayOptions.EnableProxyProtocol } else { tcpSetting := &coreConf.TCPConfig{ AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol, } //Enable proxy protocol in.StreamSetting.TCPSettings = tcpSetting } case "ws": if in.StreamSetting.WSSettings != nil { in.StreamSetting.WSSettings.AcceptProxyProtocol = option.XrayOptions.EnableProxyProtocol } else { in.StreamSetting.WSSettings = &coreConf.WebSocketConfig{ AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol, } //Enable proxy protocol } default: socketConfig := &coreConf.SocketConfig{ AcceptProxyProtocol: option.XrayOptions.EnableProxyProtocol, TFO: option.XrayOptions.EnableTFO, } //Enable proxy protocol in.StreamSetting.SocketSettings = socketConfig } // Set TLS or Reality settings switch nodeInfo.Security { case panel.Tls: // Normal tls if option.CertConfig == nil { return nil, errors.New("the CertConfig is not vail") } switch option.CertConfig.CertMode { case "none", "": break // disable default: in.StreamSetting.Security = "tls" in.StreamSetting.TLSSettings = &coreConf.TLSConfig{ Certs: []*coreConf.TLSCertConfig{ { CertFile: option.CertConfig.CertFile, KeyFile: option.CertConfig.KeyFile, OcspStapling: 3600, }, }, RejectUnknownSNI: option.CertConfig.RejectUnknownSni, } } case panel.Reality: // Reality in.StreamSetting.Security = "reality" v := nodeInfo.VAllss dest := v.TlsSettings.Dest if dest == "" { dest = v.TlsSettings.ServerName } xver := v.TlsSettings.Xver if xver == 0 { xver = v.RealityConfig.Xver } d, err := json.Marshal(fmt.Sprintf( "%s:%s", dest, v.TlsSettings.ServerPort)) if err != nil { return nil, fmt.Errorf("marshal reality dest error: %s", err) } mtd, _ := time.ParseDuration(v.RealityConfig.MaxTimeDiff) in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{ Dest: d, Xver: xver, ServerNames: []string{v.TlsSettings.ServerName}, PrivateKey: v.TlsSettings.PrivateKey, MinClientVer: v.RealityConfig.MinClientVer, MaxClientVer: v.RealityConfig.MaxClientVer, MaxTimeDiff: uint64(mtd.Microseconds()), ShortIds: []string{v.TlsSettings.ShortId}, } default: break } in.Tag = tag return in.Build() } func buildV2ray(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { v := nodeInfo.VAllss if nodeInfo.Type == "vless" { //Set vless inbound.Protocol = "vless" if config.XrayOptions.EnableFallback { // Set fallback fallbackConfigs, err := buildVlessFallbacks(config.XrayOptions.FallBackConfigs) if err != nil { return err } s, err := json.Marshal(&coreConf.VLessInboundConfig{ Decryption: "none", Fallbacks: fallbackConfigs, }) if err != nil { return fmt.Errorf("marshal vless fallback config error: %s", err) } inbound.Settings = (*json.RawMessage)(&s) } else { var err error s, err := json.Marshal(&coreConf.VLessInboundConfig{ Decryption: "none", }) if err != nil { return fmt.Errorf("marshal vless config error: %s", err) } inbound.Settings = (*json.RawMessage)(&s) } } else { // Set vmess inbound.Protocol = "vmess" var err error s, err := json.Marshal(&coreConf.VMessInboundConfig{}) if err != nil { return fmt.Errorf("marshal vmess settings error: %s", err) } inbound.Settings = (*json.RawMessage)(&s) } if len(v.NetworkSettings) == 0 { return nil } t := coreConf.TransportProtocol(nodeInfo.VAllss.Network) inbound.StreamSetting = &coreConf.StreamConfig{Network: &t} switch v.Network { case "tcp": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.TCPSettings) if err != nil { return fmt.Errorf("unmarshal tcp settings error: %s", err) } case "ws": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.WSSettings) if err != nil { return fmt.Errorf("unmarshal ws settings error: %s", err) } case "grpc": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.GRPCConfig) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } case "http": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.HTTPSettings) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } case "quic": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.QUICSettings) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } case "httpupgrade": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.HTTPUPGRADESettings) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } case "splithttp": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.SplitHTTPSettings) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } default: return errors.New("the network type is not vail") } return nil } func buildTrojan(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { inbound.Protocol = "trojan" v := nodeInfo.Trojan if config.XrayOptions.EnableFallback { // Set fallback fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs) if err != nil { return err } s, err := json.Marshal(&coreConf.TrojanServerConfig{ Fallbacks: fallbackConfigs, }) inbound.Settings = (*json.RawMessage)(&s) if err != nil { return fmt.Errorf("marshal trojan fallback config error: %s", err) } } else { s := []byte("{}") inbound.Settings = (*json.RawMessage)(&s) } network := v.Network if network == "" { network = "tcp" } t := coreConf.TransportProtocol(network) inbound.StreamSetting = &coreConf.StreamConfig{Network: &t} switch network { case "tcp": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.TCPSettings) if err != nil { return fmt.Errorf("unmarshal tcp settings error: %s", err) } case "ws": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.WSSettings) if err != nil { return fmt.Errorf("unmarshal ws settings error: %s", err) } case "grpc": err := json.Unmarshal(v.NetworkSettings, &inbound.StreamSetting.GRPCConfig) if err != nil { return fmt.Errorf("unmarshal grpc settings error: %s", err) } default: return errors.New("the network type is not vail") } return nil } func buildShadowsocks(config *conf.Options, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { inbound.Protocol = "shadowsocks" s := nodeInfo.Shadowsocks settings := &coreConf.ShadowsocksServerConfig{ Cipher: s.Cipher, } p := make([]byte, 32) _, err := rand.Read(p) if err != nil { return fmt.Errorf("generate random password error: %s", err) } randomPasswd := hex.EncodeToString(p) cipher := s.Cipher if s.ServerKey != "" { settings.Password = s.ServerKey randomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd)) cipher = "" } defaultSSuser := &coreConf.ShadowsocksUserConfig{ Cipher: cipher, Password: randomPasswd, } settings.Users = append(settings.Users, defaultSSuser) settings.NetworkList = &coreConf.NetworkList{"tcp", "udp"} settings.IVCheck = true if config.XrayOptions.DisableIVCheck { settings.IVCheck = false } t := coreConf.TransportProtocol("tcp") inbound.StreamSetting = &coreConf.StreamConfig{Network: &t} sets, err := json.Marshal(settings) inbound.Settings = (*json.RawMessage)(&sets) if err != nil { return fmt.Errorf("marshal shadowsocks settings error: %s", err) } return nil } func buildVlessFallbacks(fallbackConfigs []conf.FallBackConfigForXray) ([]*coreConf.VLessInboundFallback, error) { if fallbackConfigs == nil { return nil, fmt.Errorf("you must provide FallBackConfigs") } vlessFallBacks := make([]*coreConf.VLessInboundFallback, len(fallbackConfigs)) for i, c := range fallbackConfigs { if c.Dest == "" { return nil, fmt.Errorf("dest is required for fallback fialed") } var dest json.RawMessage dest, err := json.Marshal(c.Dest) if err != nil { return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err) } vlessFallBacks[i] = &coreConf.VLessInboundFallback{ Name: c.SNI, Alpn: c.Alpn, Path: c.Path, Dest: dest, Xver: c.ProxyProtocolVer, } } return vlessFallBacks, nil } func buildTrojanFallbacks(fallbackConfigs []conf.FallBackConfigForXray) ([]*coreConf.TrojanInboundFallback, error) { if fallbackConfigs == nil { return nil, fmt.Errorf("you must provide FallBackConfigs") } trojanFallBacks := make([]*coreConf.TrojanInboundFallback, len(fallbackConfigs)) for i, c := range fallbackConfigs { if c.Dest == "" { return nil, fmt.Errorf("dest is required for fallback fialed") } var dest json.RawMessage dest, err := json.Marshal(c.Dest) if err != nil { return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err) } trojanFallBacks[i] = &coreConf.TrojanInboundFallback{ Name: c.SNI, Alpn: c.Alpn, Path: c.Path, Dest: dest, Xver: c.ProxyProtocolVer, } } return trojanFallBacks, nil }