diff --git a/v2rayN/v2rayN/Global.cs b/v2rayN/v2rayN/Global.cs index 6ff6b444..1bebc68b 100644 --- a/v2rayN/v2rayN/Global.cs +++ b/v2rayN/v2rayN/Global.cs @@ -28,7 +28,7 @@ public const string ConfigDB = "guiNDB.db"; public const string coreConfigFileName = "config.json"; public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig"; - public const string v2raySampleServer = "v2rayN.Sample.SampleServerConfig"; + public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig"; public const string v2raySampleHttprequestFileName = "v2rayN.Sample.SampleHttprequest"; public const string v2raySampleHttpresponseFileName = "v2rayN.Sample.SampleHttpresponse"; public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_"; @@ -131,8 +131,8 @@ public static readonly List flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" }; public static readonly List networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" }; public static readonly List kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; - public static readonly List coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5" }; - public static readonly List coreTypes4VLESS = new() { "Xray" }; + public static readonly List coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" }; + public static readonly List coreTypes4VLESS = new() { "Xray", "sing_box" }; public static readonly List domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" }; public static readonly List domainMatchers = new() { "linear", "mph", "" }; public static readonly List fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" }; diff --git a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs index 1134ddd6..7e3aca47 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs @@ -1,7 +1,4 @@ using System.IO; -using System.Net; -using System.Net.NetworkInformation; -using v2rayN.Base; using v2rayN.Mode; using v2rayN.Resx; @@ -12,19 +9,6 @@ namespace v2rayN.Handler /// internal class CoreConfigHandler { - private static string SampleClient = Global.v2raySampleClient; - private static string SampleServer = Global.v2raySampleServer; - - #region Generate client configuration - - /// - /// Generate client configuration - /// - /// - /// - /// - /// - /// public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content) { content = string.Empty; @@ -41,10 +25,26 @@ namespace v2rayN.Handler { return GenerateClientCustomConfig(node, fileName, out msg); } + else if (LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box) + { + var configGenSingbox = new CoreConfigSingbox(LazyConfig.Instance.GetConfig()); + if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0) + { + return -1; + } + if (Utils.IsNullOrEmpty(fileName)) + { + content = Utils.ToJson(singboxConfig); + } + else + { + Utils.ToJsonFile(singboxConfig, fileName, false); + } + } else { - V2rayConfig? v2rayConfig = null; - if (GenerateClientConfigContent(node, false, ref v2rayConfig, out msg) != 0) + var coreConfigV2ray = new CoreConfigV2ray(LazyConfig.Instance.GetConfig()); + if (coreConfigV2ray.GenerateClientConfigContent(node, out V2rayConfig? v2rayConfig, out msg) != 0) { return -1; } @@ -67,769 +67,6 @@ namespace v2rayN.Handler return 0; } - private static int log(Config config, ref V2rayConfig v2rayConfig, bool blExport) - { - try - { - if (blExport) - { - if (config.coreBasicItem.logEnabled) - { - v2rayConfig.log.loglevel = config.coreBasicItem.loglevel; - } - else - { - v2rayConfig.log.loglevel = config.coreBasicItem.loglevel; - v2rayConfig.log.access = ""; - v2rayConfig.log.error = ""; - } - } - else - { - if (config.coreBasicItem.logEnabled) - { - var dtNow = DateTime.Now; - v2rayConfig.log.loglevel = config.coreBasicItem.loglevel; - v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); - v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); - } - else - { - v2rayConfig.log.loglevel = config.coreBasicItem.loglevel; - v2rayConfig.log.access = ""; - v2rayConfig.log.error = ""; - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int inbound(Config config, ref V2rayConfig v2rayConfig) - { - try - { - v2rayConfig.inbounds = new List(); - - Inbounds? inbound = GetInbound(config.inbound[0], Global.InboundSocks, 0, true); - v2rayConfig.inbounds.Add(inbound); - - //http - Inbounds? inbound2 = GetInbound(config.inbound[0], Global.InboundHttp, 1, false); - v2rayConfig.inbounds.Add(inbound2); - - if (config.inbound[0].allowLANConn) - { - if (config.inbound[0].newPort4LAN) - { - Inbounds inbound3 = GetInbound(config.inbound[0], Global.InboundSocks2, 2, true); - inbound3.listen = "0.0.0.0"; - v2rayConfig.inbounds.Add(inbound3); - - Inbounds inbound4 = GetInbound(config.inbound[0], Global.InboundHttp2, 3, false); - inbound4.listen = "0.0.0.0"; - v2rayConfig.inbounds.Add(inbound4); - - //auth - if (!Utils.IsNullOrEmpty(config.inbound[0].user) && !Utils.IsNullOrEmpty(config.inbound[0].pass)) - { - inbound3.settings.auth = "password"; - inbound3.settings.accounts = new List { new AccountsItem() { user = config.inbound[0].user, pass = config.inbound[0].pass } }; - - inbound4.settings.auth = "password"; - inbound4.settings.accounts = new List { new AccountsItem() { user = config.inbound[0].user, pass = config.inbound[0].pass } }; - } - } - else - { - inbound.listen = "0.0.0.0"; - inbound2.listen = "0.0.0.0"; - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static Inbounds? GetInbound(InItem inItem, string tag, int offset, bool bSocks) - { - string result = Utils.GetEmbedText(Global.v2raySampleInbound); - if (Utils.IsNullOrEmpty(result)) - { - return null; - } - - var inbound = Utils.FromJson(result); - if (inbound == null) - { - return null; - } - inbound.tag = tag; - inbound.port = inItem.localPort + offset; - inbound.protocol = bSocks ? Global.InboundSocks : Global.InboundHttp; - inbound.settings.udp = inItem.udpEnabled; - inbound.sniffing.enabled = inItem.sniffingEnabled; - inbound.sniffing.routeOnly = inItem.routeOnly; - - return inbound; - } - - private static int routing(Config config, ref V2rayConfig v2rayConfig) - { - try - { - if (v2rayConfig.routing?.rules != null) - { - v2rayConfig.routing.domainStrategy = config.routingBasicItem.domainStrategy; - v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(config.routingBasicItem.domainMatcher) ? null : config.routingBasicItem.domainMatcher; - - if (config.routingBasicItem.enableRoutingAdvanced) - { - var routing = ConfigHandler.GetDefaultRouting(ref config); - if (routing != null) - { - if (!Utils.IsNullOrEmpty(routing.domainStrategy)) - { - v2rayConfig.routing.domainStrategy = routing.domainStrategy; - } - var rules = Utils.FromJson>(routing.ruleSet); - foreach (var item in rules) - { - if (item.enabled) - { - routingUserRule(item, ref v2rayConfig); - } - } - } - } - else - { - var lockedItem = ConfigHandler.GetLockedRoutingItem(ref config); - if (lockedItem != null) - { - var rules = Utils.FromJson>(lockedItem.ruleSet); - foreach (var item in rules) - { - routingUserRule(item, ref v2rayConfig); - } - } - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int routingUserRule(RulesItem rules, ref V2rayConfig v2rayConfig) - { - try - { - if (rules == null) - { - return 0; - } - if (Utils.IsNullOrEmpty(rules.port)) - { - rules.port = null; - } - if (rules.domain?.Count == 0) - { - rules.domain = null; - } - if (rules.ip?.Count == 0) - { - rules.ip = null; - } - if (rules.protocol?.Count == 0) - { - rules.protocol = null; - } - if (rules.inboundTag?.Count == 0) - { - rules.inboundTag = null; - } - - var hasDomainIp = false; - if (rules.domain?.Count > 0) - { - var it = Utils.DeepCopy(rules); - it.ip = null; - it.type = "field"; - for (int k = it.domain.Count - 1; k >= 0; k--) - { - if (it.domain[k].StartsWith("#")) - { - it.domain.RemoveAt(k); - } - it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); - } - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (rules.ip?.Count > 0) - { - var it = Utils.DeepCopy(rules); - it.domain = null; - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (!hasDomainIp) - { - if (!Utils.IsNullOrEmpty(rules.port) - || (rules.protocol?.Count > 0) - || (rules.inboundTag?.Count > 0) - ) - { - var it = Utils.DeepCopy(rules); - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int outbound(ProfileItem node, ref V2rayConfig v2rayConfig) - { - try - { - var config = LazyConfig.Instance.GetConfig(); - Outbounds outbound = v2rayConfig.outbounds[0]; - if (node.configType == EConfigType.VMess) - { - VnextItem vnextItem; - if (outbound.settings.vnext.Count <= 0) - { - vnextItem = new VnextItem(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext[0]; - } - vnextItem.address = node.address; - vnextItem.port = node.port; - - UsersItem usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users[0]; - } - //远程服务器用户ID - usersItem.id = node.id; - usersItem.alterId = node.alterId; - usersItem.email = Global.userEMail; - if (Global.vmessSecuritys.Contains(node.security)) - { - usersItem.security = node.security; - } - else - { - usersItem.security = Global.DefaultSecurity; - } - - //Mux - outbound.mux.enabled = config.coreBasicItem.muxEnabled; - outbound.mux.concurrency = config.coreBasicItem.muxEnabled ? 8 : -1; - - boundStreamSettings(node, "out", outbound.streamSettings); - - outbound.protocol = Global.vmessProtocolLite; - outbound.settings.servers = null; - } - else if (node.configType == EConfigType.Shadowsocks) - { - ServersItem serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.password = node.id; - serversItem.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; - - serversItem.ota = false; - serversItem.level = 1; - - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; - - boundStreamSettings(node, "out", outbound.streamSettings); - - outbound.protocol = Global.ssProtocolLite; - outbound.settings.vnext = null; - } - else if (node.configType == EConfigType.Socks) - { - ServersItem serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.method = null; - serversItem.password = null; - - if (!Utils.IsNullOrEmpty(node.security) - && !Utils.IsNullOrEmpty(node.id)) - { - SocksUsersItem socksUsersItem = new() - { - user = node.security, - pass = node.id, - level = 1 - }; - - serversItem.users = new List() { socksUsersItem }; - } - - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; - - outbound.protocol = Global.socksProtocolLite; - outbound.settings.vnext = null; - } - else if (node.configType == EConfigType.VLESS) - { - VnextItem vnextItem; - if (outbound.settings.vnext.Count <= 0) - { - vnextItem = new VnextItem(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext[0]; - } - vnextItem.address = node.address; - vnextItem.port = node.port; - - UsersItem usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users[0]; - } - usersItem.id = node.id; - usersItem.flow = string.Empty; - usersItem.email = Global.userEMail; - usersItem.encryption = node.security; - - //Mux - outbound.mux.enabled = config.coreBasicItem.muxEnabled; - outbound.mux.concurrency = config.coreBasicItem.muxEnabled ? 8 : -1; - - boundStreamSettings(node, "out", outbound.streamSettings); - - if (node.streamSecurity == Global.StreamSecurityReality - || node.streamSecurity == Global.StreamSecurity) - { - if (!Utils.IsNullOrEmpty(node.flow)) - { - usersItem.flow = node.flow; - - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; - } - } - - outbound.protocol = Global.vlessProtocolLite; - outbound.settings.servers = null; - } - else if (node.configType == EConfigType.Trojan) - { - ServersItem serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.password = node.id; - serversItem.flow = string.Empty; - - serversItem.ota = false; - serversItem.level = 1; - - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; - - boundStreamSettings(node, "out", outbound.streamSettings); - - outbound.protocol = Global.trojanProtocolLite; - outbound.settings.vnext = null; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int boundStreamSettings(ProfileItem node, string iobound, StreamSettings streamSettings) - { - try - { - var config = LazyConfig.Instance.GetConfig(); - - streamSettings.network = node.GetNetwork(); - string host = node.requestHost.TrimEx(); - string sni = node.sni; - string useragent = ""; - if (!config.coreBasicItem.defUserAgent.IsNullOrEmpty()) - { - try - { - useragent = Global.userAgentTxt[config.coreBasicItem.defUserAgent]; - } - catch (KeyNotFoundException) - { - useragent = config.coreBasicItem.defUserAgent; - } - } - - //if tls - if (node.streamSecurity == Global.StreamSecurity) - { - streamSettings.security = node.streamSecurity; - - TlsSettings tlsSettings = new() - { - allowInsecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), - alpn = node.GetAlpn(), - fingerprint = node.fingerprint.IsNullOrEmpty() ? config.coreBasicItem.defFingerprint : node.fingerprint - }; - if (!string.IsNullOrWhiteSpace(sni)) - { - tlsSettings.serverName = sni; - } - else if (!string.IsNullOrWhiteSpace(host)) - { - tlsSettings.serverName = Utils.String2List(host)[0]; - } - streamSettings.tlsSettings = tlsSettings; - } - - //if Reality - if (node.streamSecurity == Global.StreamSecurityReality) - { - streamSettings.security = node.streamSecurity; - - TlsSettings realitySettings = new() - { - fingerprint = node.fingerprint.IsNullOrEmpty() ? config.coreBasicItem.defFingerprint : node.fingerprint, - serverName = sni, - publicKey = node.publicKey, - shortId = node.shortId, - spiderX = node.spiderX, - }; - - streamSettings.realitySettings = realitySettings; - } - - //streamSettings - switch (node.GetNetwork()) - { - case "kcp": - KcpSettings kcpSettings = new() - { - mtu = config.kcpItem.mtu, - tti = config.kcpItem.tti - }; - if (iobound == "out") - { - kcpSettings.uplinkCapacity = config.kcpItem.uplinkCapacity; - kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity; - } - else if (iobound == "in") - { - kcpSettings.uplinkCapacity = config.kcpItem.downlinkCapacity; ; - kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity; - } - else - { - kcpSettings.uplinkCapacity = config.kcpItem.uplinkCapacity; - kcpSettings.downlinkCapacity = config.kcpItem.downlinkCapacity; - } - - kcpSettings.congestion = config.kcpItem.congestion; - kcpSettings.readBufferSize = config.kcpItem.readBufferSize; - kcpSettings.writeBufferSize = config.kcpItem.writeBufferSize; - kcpSettings.header = new Header - { - type = node.headerType - }; - if (!Utils.IsNullOrEmpty(node.path)) - { - kcpSettings.seed = node.path; - } - streamSettings.kcpSettings = kcpSettings; - break; - //ws - case "ws": - WsSettings wsSettings = new(); - wsSettings.headers = new Headers(); - string path = node.path; - if (!string.IsNullOrWhiteSpace(host)) - { - wsSettings.headers.Host = host; - } - if (!string.IsNullOrWhiteSpace(path)) - { - wsSettings.path = path; - } - if (!string.IsNullOrWhiteSpace(useragent)) - { - wsSettings.headers.UserAgent = useragent; - } - streamSettings.wsSettings = wsSettings; - - break; - //h2 - case "h2": - HttpSettings httpSettings = new(); - - if (!string.IsNullOrWhiteSpace(host)) - { - httpSettings.host = Utils.String2List(host); - } - httpSettings.path = node.path; - - streamSettings.httpSettings = httpSettings; - - break; - //quic - case "quic": - QuicSettings quicsettings = new() - { - security = host, - key = node.path, - header = new Header - { - type = node.headerType - } - }; - streamSettings.quicSettings = quicsettings; - if (node.streamSecurity == Global.StreamSecurity) - { - if (!string.IsNullOrWhiteSpace(sni)) - { - streamSettings.tlsSettings.serverName = sni; - } - else - { - streamSettings.tlsSettings.serverName = node.address; - } - } - break; - - case "grpc": - GrpcSettings grpcSettings = new() - { - serviceName = node.path, - multiMode = (node.headerType == Global.GrpcmultiMode), - idle_timeout = config.grpcItem.idle_timeout, - health_check_timeout = config.grpcItem.health_check_timeout, - permit_without_stream = config.grpcItem.permit_without_stream, - initial_windows_size = config.grpcItem.initial_windows_size, - }; - streamSettings.grpcSettings = grpcSettings; - break; - - default: - //tcp - if (node.headerType == Global.TcpHeaderHttp) - { - TcpSettings tcpSettings = new() - { - header = new Header - { - type = node.headerType - } - }; - - if (iobound == "out") - { - //request Host - string request = Utils.GetEmbedText(Global.v2raySampleHttprequestFileName); - string[] arrHost = host.Split(','); - string host2 = string.Join("\",\"", arrHost); - request = request.Replace("$requestHost$", $"\"{host2}\""); - //request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost())); - request = request.Replace("$requestUserAgent$", $"\"{useragent}\""); - //Path - string pathHttp = @"/"; - if (!Utils.IsNullOrEmpty(node.path)) - { - string[] arrPath = node.path.Split(','); - pathHttp = string.Join("\",\"", arrPath); - } - request = request.Replace("$requestPath$", $"\"{pathHttp}\""); - tcpSettings.header.request = Utils.FromJson(request); - } - else if (iobound == "in") - { - //string response = Utils.GetEmbedText(Global.v2raySampleHttpresponseFileName); - //tcpSettings.header.response = Utils.FromJson(response); - } - - streamSettings.tcpSettings = tcpSettings; - } - break; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int dns(Config config, ref V2rayConfig v2rayConfig) - { - try - { - if (string.IsNullOrWhiteSpace(config.remoteDNS)) - { - return 0; - } - - //Outbound Freedom domainStrategy - if (!string.IsNullOrWhiteSpace(config.domainStrategy4Freedom)) - { - var outbound = v2rayConfig.outbounds[1]; - outbound.settings.domainStrategy = config.domainStrategy4Freedom; - outbound.settings.userLevel = 0; - } - - var obj = Utils.ParseJson(config.remoteDNS); - if (obj?.ContainsKey("servers") == true) - { - v2rayConfig.dns = obj; - } - else - { - List servers = new(); - - string[] arrDNS = config.remoteDNS.Split(','); - foreach (string str in arrDNS) - { - //if (Utils.IsIP(str)) - //{ - servers.Add(str); - //} - } - //servers.Add("localhost"); - v2rayConfig.dns = new Mode.Dns - { - servers = servers - }; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int statistic(Config config, ref V2rayConfig v2rayConfig) - { - if (config.guiItem.enableStatistics) - { - string tag = Global.InboundAPITagName; - API apiObj = new(); - Policy policyObj = new(); - SystemPolicy policySystemSetting = new(); - - string[] services = { "StatsService" }; - - v2rayConfig.stats = new Stats(); - - apiObj.tag = tag; - apiObj.services = services.ToList(); - v2rayConfig.api = apiObj; - - policySystemSetting.statsOutboundDownlink = true; - policySystemSetting.statsOutboundUplink = true; - policyObj.system = policySystemSetting; - v2rayConfig.policy = policyObj; - - if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) - { - Inbounds apiInbound = new(); - Inboundsettings apiInboundSettings = new(); - apiInbound.tag = tag; - apiInbound.listen = Global.Loopback; - apiInbound.port = Global.statePort; - apiInbound.protocol = Global.InboundAPIProtocal; - apiInboundSettings.address = Global.Loopback; - apiInbound.settings = apiInboundSettings; - v2rayConfig.inbounds.Add(apiInbound); - } - - if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) - { - RulesItem apiRoutingRule = new() - { - inboundTag = new List { tag }, - outboundTag = tag, - type = "field" - }; - v2rayConfig.routing.rules.Add(apiRoutingRule); - } - } - return 0; - } - - /// - /// Generate custom configuration - /// - /// - /// - /// - /// private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg) { try @@ -909,590 +146,10 @@ namespace v2rayN.Handler return 0; } - public static int GenerateClientConfigContent(ProfileItem node, bool blExport, ref V2rayConfig? v2rayConfig, out string msg) - { - try - { - if (node == null - || node.port <= 0) - { - msg = ResUI.CheckServerSettings; - return -1; - } - - msg = ResUI.InitialConfiguration; - - string result = Utils.GetEmbedText(SampleClient); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return -1; - } - - v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - - var config = LazyConfig.Instance.GetConfig(); - - log(config, ref v2rayConfig, blExport); - - inbound(config, ref v2rayConfig); - - routing(config, ref v2rayConfig); - - //outbound - outbound(node, ref v2rayConfig); - - //dns - dns(config, ref v2rayConfig); - - //stat - statistic(config, ref v2rayConfig); - - msg = string.Format(ResUI.SuccessfulConfiguration, ""); - } - catch (Exception ex) - { - Utils.SaveLog("GenerateClientConfig", ex); - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - return 0; - } - - #endregion Generate client configuration - - #region Generate server-side configuration - - public static int GenerateServerConfig(ProfileItem node, string fileName, out string msg) - { - try - { - if (node == null) - { - msg = ResUI.CheckServerSettings; - return -1; - } - - msg = ResUI.InitialConfiguration; - - string result = Utils.GetEmbedText(SampleServer); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return -1; - } - - V2rayConfig? v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - - var config = LazyConfig.Instance.GetConfig(); - - log(config, ref v2rayConfig, true); - - ServerInbound(node, ref v2rayConfig); - - ServerOutbound(config, ref v2rayConfig); - - Utils.ToJsonFile(v2rayConfig, fileName, false); - - msg = string.Format(ResUI.SuccessfulConfiguration, node.GetSummary()); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - return 0; - } - - private static int ServerInbound(ProfileItem node, ref V2rayConfig v2rayConfig) - { - try - { - Inbounds inbound = v2rayConfig.inbounds[0]; - UsersItem usersItem; - if (inbound.settings.clients.Count <= 0) - { - usersItem = new UsersItem(); - inbound.settings.clients.Add(usersItem); - } - else - { - usersItem = inbound.settings.clients[0]; - } - inbound.port = node.port; - - usersItem.id = node.id; - usersItem.email = Global.userEMail; - - if (node.configType == EConfigType.VMess) - { - inbound.protocol = Global.vmessProtocolLite; - usersItem.alterId = node.alterId; - } - else if (node.configType == EConfigType.VLESS) - { - inbound.protocol = Global.vlessProtocolLite; - usersItem.flow = node.flow; - inbound.settings.decryption = node.security; - } - - boundStreamSettings(node, "in", inbound.streamSettings); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private static int ServerOutbound(Config config, ref V2rayConfig v2rayConfig) - { - try - { - if (v2rayConfig.outbounds[0] != null) - { - v2rayConfig.outbounds[0].settings = null; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - #endregion Generate server-side configuration - - #region Import (export) client/server configuration - - public static ProfileItem? ImportFromClientConfig(string fileName, out string msg) - { - msg = string.Empty; - ProfileItem profileItem = new(); - - try - { - string result = Utils.LoadResource(fileName); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedReadConfiguration; - return null; - } - - V2rayConfig? v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedConversionConfiguration; - return null; - } - - if (v2rayConfig.outbounds == null - || v2rayConfig.outbounds.Count <= 0) - { - msg = ResUI.IncorrectClientConfiguration; - return null; - } - - Outbounds outbound = v2rayConfig.outbounds[0]; - if (outbound == null - || Utils.IsNullOrEmpty(outbound.protocol) - || outbound.protocol != Global.vmessProtocolLite - || outbound.settings == null - || outbound.settings.vnext == null - || outbound.settings.vnext.Count <= 0 - || outbound.settings.vnext[0].users == null - || outbound.settings.vnext[0].users.Count <= 0) - { - msg = ResUI.IncorrectClientConfiguration; - return null; - } - - profileItem.security = Global.DefaultSecurity; - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - profileItem.address = outbound.settings.vnext[0].address; - profileItem.port = outbound.settings.vnext[0].port; - profileItem.id = outbound.settings.vnext[0].users[0].id; - profileItem.alterId = outbound.settings.vnext[0].users[0].alterId; - profileItem.remarks = $"import@{DateTime.Now.ToShortDateString()}"; - - //tcp or kcp - if (outbound.streamSettings?.network != null - && !Utils.IsNullOrEmpty(outbound.streamSettings.network)) - { - profileItem.network = outbound.streamSettings.network; - } - - //tcp http - if (outbound.streamSettings?.tcpSettings?.header != null - && !Utils.IsNullOrEmpty(outbound.streamSettings.tcpSettings.header.type)) - { - if (outbound.streamSettings.tcpSettings.header.type == Global.TcpHeaderHttp) - { - profileItem.headerType = outbound.streamSettings.tcpSettings.header.type; - string? request = Convert.ToString(outbound.streamSettings.tcpSettings.header.request); - if (!Utils.IsNullOrEmpty(request)) - { - V2rayTcpRequest? v2rayTcpRequest = Utils.FromJson(request); - if (v2rayTcpRequest?.headers?.Host?.Count > 0) - { - profileItem.requestHost = v2rayTcpRequest.headers.Host[0]; - } - } - } - } - //kcp - if (outbound?.streamSettings?.kcpSettings?.header != null - && !Utils.IsNullOrEmpty(outbound.streamSettings.kcpSettings.header.type)) - { - profileItem.headerType = outbound.streamSettings.kcpSettings.header.type; - } - - //ws - if (outbound?.streamSettings?.wsSettings != null) - { - if (!Utils.IsNullOrEmpty(outbound.streamSettings.wsSettings.path)) - { - profileItem.path = outbound.streamSettings.wsSettings.path; - } - if (outbound.streamSettings.wsSettings.headers != null - && !Utils.IsNullOrEmpty(outbound.streamSettings.wsSettings.headers.Host)) - { - profileItem.requestHost = outbound.streamSettings.wsSettings.headers.Host; - } - } - - //h2 - if (outbound?.streamSettings?.httpSettings != null) - { - if (!Utils.IsNullOrEmpty(outbound.streamSettings.httpSettings.path)) - { - profileItem.path = outbound.streamSettings.httpSettings.path; - } - if (outbound.streamSettings.httpSettings.host?.Count > 0) - { - profileItem.requestHost = Utils.List2String(outbound.streamSettings.httpSettings.host); - } - } - - //tls - if (outbound?.streamSettings?.security == Global.StreamSecurity) - { - profileItem.streamSecurity = Global.StreamSecurity; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - msg = ResUI.IncorrectClientConfiguration; - return null; - } - - return profileItem; - } - - public static ProfileItem? ImportFromServerConfig(string fileName, out string msg) - { - msg = string.Empty; - ProfileItem profileItem = new(); - - try - { - string? result = Utils.LoadResource(fileName); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedReadConfiguration; - return null; - } - - V2rayConfig? v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedConversionConfiguration; - return null; - } - - if (v2rayConfig.inbounds == null - || v2rayConfig.inbounds.Count <= 0) - { - msg = ResUI.IncorrectServerConfiguration; - return null; - } - - Inbounds inbound = v2rayConfig.inbounds[0]; - if (inbound == null - || Utils.IsNullOrEmpty(inbound.protocol) - || inbound.protocol != Global.vmessProtocolLite - || inbound.settings == null - || inbound.settings.clients == null - || inbound.settings.clients.Count <= 0) - { - msg = ResUI.IncorrectServerConfiguration; - return null; - } - - profileItem.security = Global.DefaultSecurity; - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - profileItem.address = string.Empty; - profileItem.port = inbound.port; - profileItem.id = inbound.settings.clients[0].id; - profileItem.alterId = inbound.settings.clients[0].alterId; - - profileItem.remarks = $"import@{DateTime.Now.ToShortDateString()}"; - - //tcp or kcp - if (inbound.streamSettings?.network != null - && !Utils.IsNullOrEmpty(inbound.streamSettings.network)) - { - profileItem.network = inbound.streamSettings.network; - } - - //tcp http - if (inbound.streamSettings?.tcpSettings?.header != null - && !Utils.IsNullOrEmpty(inbound.streamSettings.tcpSettings.header.type)) - { - if (inbound.streamSettings.tcpSettings.header.type == Global.TcpHeaderHttp) - { - profileItem.headerType = inbound.streamSettings.tcpSettings.header.type; - string? request = Convert.ToString(inbound.streamSettings.tcpSettings.header.request); - if (!Utils.IsNullOrEmpty(request)) - { - V2rayTcpRequest? v2rayTcpRequest = Utils.FromJson(request); - if (v2rayTcpRequest?.headers?.Host?.Count > 0) - { - profileItem.requestHost = v2rayTcpRequest.headers.Host[0]; - } - } - } - } - //kcp - //if (v2rayConfig.outbound.streamSettings != null - // && v2rayConfig.outbound.streamSettings.kcpSettings != null - // && v2rayConfig.outbound.streamSettings.kcpSettings.header != null - // && !Utils.IsNullOrEmpty(v2rayConfig.outbound.streamSettings.kcpSettings.header.type)) - //{ - // cmbHeaderType.Text = v2rayConfig.outbound.streamSettings.kcpSettings.header.type; - //} - - //ws - if (inbound.streamSettings?.wsSettings != null) - { - if (!Utils.IsNullOrEmpty(inbound.streamSettings.wsSettings.path)) - { - profileItem.path = inbound.streamSettings.wsSettings.path; - } - if (inbound.streamSettings.wsSettings.headers != null - && !Utils.IsNullOrEmpty(inbound.streamSettings.wsSettings.headers.Host)) - { - profileItem.requestHost = inbound.streamSettings.wsSettings.headers.Host; - } - } - - //h2 - if (inbound.streamSettings?.httpSettings != null) - { - if (!Utils.IsNullOrEmpty(inbound.streamSettings.httpSettings.path)) - { - profileItem.path = inbound.streamSettings.httpSettings.path; - } - if (inbound.streamSettings.httpSettings.host?.Count > 0) - { - profileItem.requestHost = Utils.List2String(inbound.streamSettings.httpSettings.host); - } - } - - //tls - if (inbound.streamSettings?.security == Global.StreamSecurity) - { - profileItem.streamSecurity = Global.StreamSecurity; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - msg = ResUI.IncorrectClientConfiguration; - return null; - } - return profileItem; - } - - public static int Export2ClientConfig(ProfileItem node, string fileName, out string msg) - { - V2rayConfig? v2rayConfig = null; - if (GenerateClientConfigContent(node, true, ref v2rayConfig, out msg) != 0) - { - return -1; - } - return Utils.ToJsonFile(v2rayConfig, fileName, false); - } - - public static int Export2ServerConfig(ProfileItem node, string fileName, out string msg) - { - return GenerateServerConfig(node, fileName, out msg); - } - - #endregion Import (export) client/server configuration - - #region Gen speedtest config - public static string GenerateClientSpeedtestConfigString(Config config, List selecteds, out string msg) { - try - { - if (config == null) - { - msg = ResUI.CheckServerSettings; - return ""; - } - - msg = ResUI.InitialConfiguration; - - Config configCopy = Utils.DeepCopy(config); - - string result = Utils.GetEmbedText(SampleClient); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return ""; - } - - V2rayConfig? v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return ""; - } - List lstIpEndPoints = new(); - List lstTcpConns = new(); - try - { - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); - lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - - log(configCopy, ref v2rayConfig, true); - //routing(config, ref v2rayConfig); - //dns(configCopy, ref v2rayConfig); - - v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. - - int httpPort = LazyConfig.Instance.GetLocalPort("speedtest"); - - foreach (var it in selecteds) - { - if (it.configType == EConfigType.Custom) - { - continue; - } - if (it.port <= 0) - { - continue; - } - if (it.configType is EConfigType.VMess or EConfigType.VLESS) - { - var item2 = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item2 is null || Utils.IsNullOrEmpty(item2.id) || !Utils.IsGuidByParse(item2.id)) - { - continue; - } - } - - //find unuse port - var port = httpPort; - for (int k = httpPort; k < Global.MaxPort; k++) - { - if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) - { - continue; - } - if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) - { - continue; - } - //found - port = k; - httpPort = port + 1; - break; - } - - //Port In Used - if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) - { - continue; - } - it.port = port; - it.allowTest = true; - - //inbound - Inbounds inbound = new() - { - listen = Global.Loopback, - port = port, - protocol = Global.InboundHttp - }; - inbound.tag = Global.InboundHttp + inbound.port.ToString(); - v2rayConfig.inbounds.Add(inbound); - - //outbound - V2rayConfig? v2rayConfigCopy = Utils.FromJson(result); - var item = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item is null) - { - continue; - } - if (item.configType == EConfigType.Shadowsocks - && !Global.ssSecuritysInXray.Contains(item.security)) - { - continue; - } - if (item.configType == EConfigType.VLESS - && !Global.flows.Contains(item.flow)) - { - continue; - } - - outbound(item, ref v2rayConfigCopy); - v2rayConfigCopy.outbounds[0].tag = Global.agentTag + inbound.port.ToString(); - v2rayConfig.outbounds.Add(v2rayConfigCopy.outbounds[0]); - - //rule - RulesItem rule = new() - { - inboundTag = new List { inbound.tag }, - outboundTag = v2rayConfigCopy.outbounds[0].tag, - type = "field" - }; - v2rayConfig.routing.rules.Add(rule); - } - - //msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); - return Utils.ToJson(v2rayConfig); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - msg = ResUI.FailedGenDefaultConfiguration; - return ""; - } + var coreConfigV2ray = new CoreConfigV2ray(config); + return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg); } - - #endregion Gen speedtest config } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs b/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs new file mode 100644 index 00000000..177c6a3e --- /dev/null +++ b/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs @@ -0,0 +1,513 @@ +using v2rayN.Base; +using v2rayN.Mode; +using v2rayN.Resx; + +namespace v2rayN.Handler +{ + internal class CoreConfigSingbox + { + private string SampleClient = Global.SingboxSampleClient; + private Config _config; + + public CoreConfigSingbox(Config config) + { + _config = config; + } + + public int GenerateClientConfigContent(ProfileItem node, out SingboxConfig? singboxConfig, out string msg) + { + singboxConfig = null; + try + { + if (node == null + || node.port <= 0) + { + msg = ResUI.CheckServerSettings; + return -1; + } + + msg = ResUI.InitialConfiguration; + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return -1; + } + + singboxConfig = Utils.FromJson(result); + if (singboxConfig == null) + { + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + + log(singboxConfig); + + inbound(singboxConfig); + + outbound(node, singboxConfig); + + routing(singboxConfig); + + //dns(singboxConfig); + + //statistic(singboxConfig); + + msg = string.Format(ResUI.SuccessfulConfiguration, ""); + } + catch (Exception ex) + { + Utils.SaveLog("GenerateClientConfig4Singbox", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + return 0; + } + + private int log(SingboxConfig singboxConfig) + { + try + { + switch (_config.coreBasicItem.loglevel) + { + case "debug": + case "info": + case "error": + singboxConfig.log.level = _config.coreBasicItem.loglevel; + break; + + default: + break; + } + if (_config.coreBasicItem.loglevel == "none") + { + singboxConfig.log.disabled = true; + } + if (_config.coreBasicItem.logEnabled) + { + var dtNow = DateTime.Now; + singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt"); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int inbound(SingboxConfig singboxConfig) + { + try + { + var inbound = singboxConfig.inbounds[0]; + inbound.listen_port = LazyConfig.Instance.GetLocalPort(Global.InboundSocks); + inbound.sniff = _config.inbound[0].sniffingEnabled; + inbound.sniff_override_destination = _config.inbound[0].sniffingEnabled; + + //http + var inbound2 = Utils.DeepCopy(inbound); + inbound2.listen_port = LazyConfig.Instance.GetLocalPort(Global.InboundHttp); + inbound2.type = Global.InboundHttp; + inbound2.tag = Global.InboundHttp; + singboxConfig.inbounds.Add(inbound2); + + if (_config.inbound[0].allowLANConn) + { + if (_config.inbound[0].newPort4LAN) + { + } + else + { + inbound.listen = "::"; + inbound2.listen = "::"; + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + #region outbound private + + private int outbound(ProfileItem node, SingboxConfig singboxConfig) + { + try + { + var outbound = singboxConfig.outbounds[0]; + outbound.server = node.address; + outbound.server_port = node.port; + + if (node.configType == EConfigType.VMess) + { + outbound.type = Global.vmessProtocolLite; + + outbound.uuid = node.id; + outbound.alter_id = node.alterId; + if (Global.vmessSecuritys.Contains(node.security)) + { + outbound.security = node.security; + } + else + { + outbound.security = Global.DefaultSecurity; + } + + outboundMux(node, outbound); + } + else if (node.configType == EConfigType.Shadowsocks) + { + outbound.type = Global.ssProtocolLite; + + outbound.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; + outbound.password = node.id; + + outboundMux(node, outbound); + } + else if (node.configType == EConfigType.Socks) + { + outbound.type = Global.socksProtocolLite; + + outbound.version = "5"; + if (!Utils.IsNullOrEmpty(node.security) + && !Utils.IsNullOrEmpty(node.id)) + { + outbound.username = node.security; + outbound.password = node.id; + } + } + else if (node.configType == EConfigType.VLESS) + { + outbound.type = Global.vlessProtocolLite; + + outbound.uuid = node.id; + outbound.flow = node.flow; + + outbound.packet_encoding = "xudp"; + } + else if (node.configType == EConfigType.Trojan) + { + outbound.type = Global.trojanProtocolLite; + + outbound.password = node.id; + + outboundMux(node, outbound); + } + + outboundTls(node, outbound); + + outboundTransport(node, outbound); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundMux(ProfileItem node, Outbound4Sbox outbound) + { + try + { + if (_config.coreBasicItem.muxEnabled) + { + var mux = new Multiplex4Sbox() + { + enabled = true, + protocol = "smux", + max_connections = 4, + min_streams = 4, + max_streams = 0, + }; + outbound.multiplex = mux; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundTls(ProfileItem node, Outbound4Sbox outbound) + { + try + { + if (node.streamSecurity == Global.StreamSecurityReality || node.streamSecurity == Global.StreamSecurity) + { + var tls = new Tls4Sbox() + { + enabled = true, + server_name = node.sni, + insecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), + alpn = node.GetAlpn(), + }; + if (!Utils.IsNullOrEmpty(node.fingerprint)) + { + tls.utls = new Utls4Sbox() + { + enabled = true, + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint + }; + } + if (node.streamSecurity == Global.StreamSecurityReality) + { + tls.reality = new Reality4Sbox() + { + enabled = true, + public_key = node.publicKey, + short_id = node.shortId + }; + } + outbound.tls = tls; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundTransport(ProfileItem node, Outbound4Sbox outbound) + { + try + { + var transport = new Transport4Sbox(); + + switch (node.GetNetwork()) + { + case "h2": + transport.type = "http"; + transport.host = Utils.IsNullOrEmpty(node.requestHost) ? null : Utils.String2List(node.requestHost); + transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; + break; + + case "ws": + transport.type = "ws"; + transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; + break; + + case "quic": + transport.type = "quic"; + break; + + case "grpc": + transport.type = "grpc"; + transport.service_name = node.path; + transport.idle_timeout = _config.grpcItem.idle_timeout.ToString("##s"); + transport.ping_timeout = _config.grpcItem.health_check_timeout.ToString("##s"); + transport.permit_without_stream = _config.grpcItem.permit_without_stream; + break; + + default: + transport = null; + break; + } + + outbound.transport = transport; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + #endregion outbound private + + #region routing rule private + + private int routing(SingboxConfig singboxConfig) + { + try + { + var routing = ConfigHandler.GetDefaultRouting(ref _config); + if (routing != null) + { + var rules = Utils.FromJson>(routing.ruleSet); + foreach (var item in rules!) + { + if (item.enabled) + { + routingUserRule(item, singboxConfig.route.rules); + } + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int routingUserRule(RulesItem item, List rules) + { + try + { + if (item == null) + { + return 0; + } + + var rule = new Rule4Sbox() + { + outbound = item.outboundTag, + }; + + if (!Utils.IsNullOrEmpty(item.port)) + { + if (item.port.Contains("-")) + { + rule.port_range = new List { item.port.Replace("-", ":") }; + } + else + { + rule.port = new List { Utils.ToInt(item.port) }; + } + } + if (item.protocol?.Count > 0) + { + rule.protocol = item.protocol; + } + if (item.inboundTag?.Count >= 0) + { + rule.inbound = item.inboundTag; + } + var rule2 = Utils.DeepCopy(rule); + + var hasDomainIp = false; + if (item.domain?.Count > 0) + { + foreach (var it in item.domain) + { + parseV2Domain(it, rule); + } + rules.Add(rule); + hasDomainIp = true; + } + + if (item.ip?.Count > 0) + { + foreach (var it in item.ip) + { + parseV2Address(it, rule2); + } + rules.Add(rule2); + hasDomainIp = true; + } + + if (!hasDomainIp) + { + rules.Add(rule); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private void parseV2Domain(string domain, Rule4Sbox rule) + { + if (domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) + { + return; + } + else if (domain.StartsWith("geosite:")) + { + if (rule.geosite is null) { rule.geosite = new(); } + rule.geosite?.Add(domain.Substring(8)); + } + else if (domain.StartsWith("regexp:")) + { + if (rule.domain_regex is null) { rule.domain_regex = new(); } + rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7)); + } + else if (domain.StartsWith("domain:")) + { + if (rule.domain is null) { rule.domain = new(); } + if (rule.domain_suffix is null) { rule.domain_suffix = new(); } + rule.domain?.Add(domain.Substring(7)); + rule.domain_suffix?.Add("." + domain.Substring(7)); + } + else if (domain.StartsWith("full:")) + { + if (rule.domain is null) { rule.domain = new(); } + rule.domain?.Add(domain.Substring(5)); + } + else if (domain.StartsWith("keyword:")) + { + if (rule.domain_keyword is null) { rule.domain_keyword = new(); } + rule.domain_keyword?.Add(domain.Substring(8)); + } + else + { + if (rule.domain_keyword is null) { rule.domain_keyword = new(); } + rule.domain_keyword?.Add(domain); + } + } + + private void parseV2Address(string address, Rule4Sbox rule) + { + if (address.StartsWith("ext:") || address.StartsWith("ext-ip:")) + { + return; + } + else if (address.StartsWith("geoip:!")) + { + return; + } + else if (address.StartsWith("geoip:")) + { + if (rule.geoip is null) { rule.geoip = new(); } + rule.geoip?.Add(address.Substring(6)); + } + else + { + if (rule.ip_cidr is null) { rule.ip_cidr = new(); } + rule.ip_cidr?.Add(address); + } + } + + #endregion routing rule private + + private int dns(SingboxConfig singboxConfig) + { + try + { + //singboxConfig.dns = new(); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int statistic(SingboxConfig singboxConfig) + { + if (_config.guiItem.enableStatistics) + { + singboxConfig.experimental = new Experimental4Sbox() + { + v2ray_api = new V2ray_Api4Sbox() + { + listen = $"{Global.Loopback}:{Global.statePort}", + stats = new Stats4Sbox() + { + enabled = true, + } + } + }; + } + return 0; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs b/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs new file mode 100644 index 00000000..57815f05 --- /dev/null +++ b/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs @@ -0,0 +1,927 @@ +using System.Net; +using System.Net.NetworkInformation; +using v2rayN.Base; +using v2rayN.Mode; +using v2rayN.Resx; + +namespace v2rayN.Handler +{ + internal class CoreConfigV2ray + { + private string SampleClient = Global.v2raySampleClient; + private Config _config; + + public CoreConfigV2ray(Config config) + { + _config = config; + } + + public int GenerateClientConfigContent(ProfileItem node, out V2rayConfig? v2rayConfig, out string msg) + { + v2rayConfig = null; + try + { + if (node == null + || node.port <= 0) + { + msg = ResUI.CheckServerSettings; + return -1; + } + + msg = ResUI.InitialConfiguration; + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return -1; + } + + v2rayConfig = Utils.FromJson(result); + if (v2rayConfig == null) + { + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + + log(v2rayConfig); + + inbound(v2rayConfig); + + routing(v2rayConfig); + + outbound(node, v2rayConfig); + + dns(v2rayConfig); + + statistic(v2rayConfig); + + msg = string.Format(ResUI.SuccessfulConfiguration, ""); + } + catch (Exception ex) + { + Utils.SaveLog("GenerateClientConfig4V2ray", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + return 0; + } + + private int log(V2rayConfig v2rayConfig) + { + try + { + if (_config.coreBasicItem.logEnabled) + { + var dtNow = DateTime.Now; + v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; + v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); + v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); + } + else + { + v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; + v2rayConfig.log.access = ""; + v2rayConfig.log.error = ""; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int inbound(V2rayConfig v2rayConfig) + { + try + { + v2rayConfig.inbounds = new List(); + + Inbounds? inbound = GetInbound(_config.inbound[0], Global.InboundSocks, 0, true); + v2rayConfig.inbounds.Add(inbound); + + //http + Inbounds? inbound2 = GetInbound(_config.inbound[0], Global.InboundHttp, 1, false); + v2rayConfig.inbounds.Add(inbound2); + + if (_config.inbound[0].allowLANConn) + { + if (_config.inbound[0].newPort4LAN) + { + Inbounds inbound3 = GetInbound(_config.inbound[0], Global.InboundSocks2, 2, true); + inbound3.listen = "0.0.0.0"; + v2rayConfig.inbounds.Add(inbound3); + + Inbounds inbound4 = GetInbound(_config.inbound[0], Global.InboundHttp2, 3, false); + inbound4.listen = "0.0.0.0"; + v2rayConfig.inbounds.Add(inbound4); + + //auth + if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass)) + { + inbound3.settings.auth = "password"; + inbound3.settings.accounts = new List { new AccountsItem() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; + + inbound4.settings.auth = "password"; + inbound4.settings.accounts = new List { new AccountsItem() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; + } + } + else + { + inbound.listen = "0.0.0.0"; + inbound2.listen = "0.0.0.0"; + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private Inbounds? GetInbound(InItem inItem, string tag, int offset, bool bSocks) + { + string result = Utils.GetEmbedText(Global.v2raySampleInbound); + if (Utils.IsNullOrEmpty(result)) + { + return null; + } + + var inbound = Utils.FromJson(result); + if (inbound == null) + { + return null; + } + inbound.tag = tag; + inbound.port = inItem.localPort + offset; + inbound.protocol = bSocks ? Global.InboundSocks : Global.InboundHttp; + inbound.settings.udp = inItem.udpEnabled; + inbound.sniffing.enabled = inItem.sniffingEnabled; + inbound.sniffing.routeOnly = inItem.routeOnly; + + return inbound; + } + + private int routing(V2rayConfig v2rayConfig) + { + try + { + if (v2rayConfig.routing?.rules != null) + { + v2rayConfig.routing.domainStrategy = _config.routingBasicItem.domainStrategy; + v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.routingBasicItem.domainMatcher) ? null : _config.routingBasicItem.domainMatcher; + + if (_config.routingBasicItem.enableRoutingAdvanced) + { + var routing = ConfigHandler.GetDefaultRouting(ref _config); + if (routing != null) + { + if (!Utils.IsNullOrEmpty(routing.domainStrategy)) + { + v2rayConfig.routing.domainStrategy = routing.domainStrategy; + } + var rules = Utils.FromJson>(routing.ruleSet); + foreach (var item in rules) + { + if (item.enabled) + { + routingUserRule(item, v2rayConfig); + } + } + } + } + else + { + var lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); + if (lockedItem != null) + { + var rules = Utils.FromJson>(lockedItem.ruleSet); + foreach (var item in rules) + { + routingUserRule(item, v2rayConfig); + } + } + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int routingUserRule(RulesItem rules, V2rayConfig v2rayConfig) + { + try + { + if (rules == null) + { + return 0; + } + if (Utils.IsNullOrEmpty(rules.port)) + { + rules.port = null; + } + if (rules.domain?.Count == 0) + { + rules.domain = null; + } + if (rules.ip?.Count == 0) + { + rules.ip = null; + } + if (rules.protocol?.Count == 0) + { + rules.protocol = null; + } + if (rules.inboundTag?.Count == 0) + { + rules.inboundTag = null; + } + + var hasDomainIp = false; + if (rules.domain?.Count > 0) + { + var it = Utils.DeepCopy(rules); + it.ip = null; + it.type = "field"; + for (int k = it.domain.Count - 1; k >= 0; k--) + { + if (it.domain[k].StartsWith("#")) + { + it.domain.RemoveAt(k); + } + it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); + } + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (rules.ip?.Count > 0) + { + var it = Utils.DeepCopy(rules); + it.domain = null; + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (!hasDomainIp) + { + if (!Utils.IsNullOrEmpty(rules.port) + || (rules.protocol?.Count > 0) + || (rules.inboundTag?.Count > 0) + ) + { + var it = Utils.DeepCopy(rules); + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outbound(ProfileItem node, V2rayConfig v2rayConfig) + { + try + { + Outbounds outbound = v2rayConfig.outbounds[0]; + if (node.configType == EConfigType.VMess) + { + VnextItem vnextItem; + if (outbound.settings.vnext.Count <= 0) + { + vnextItem = new VnextItem(); + outbound.settings.vnext.Add(vnextItem); + } + else + { + vnextItem = outbound.settings.vnext[0]; + } + vnextItem.address = node.address; + vnextItem.port = node.port; + + UsersItem usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users[0]; + } + //远程服务器用户ID + usersItem.id = node.id; + usersItem.alterId = node.alterId; + usersItem.email = Global.userEMail; + if (Global.vmessSecuritys.Contains(node.security)) + { + usersItem.security = node.security; + } + else + { + usersItem.security = Global.DefaultSecurity; + } + + //Mux + outbound.mux.enabled = _config.coreBasicItem.muxEnabled; + outbound.mux.concurrency = _config.coreBasicItem.muxEnabled ? 8 : -1; + + outbound.protocol = Global.vmessProtocolLite; + outbound.settings.servers = null; + } + else if (node.configType == EConfigType.Shadowsocks) + { + ServersItem serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.password = node.id; + serversItem.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; + + serversItem.ota = false; + serversItem.level = 1; + + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + + outbound.protocol = Global.ssProtocolLite; + outbound.settings.vnext = null; + } + else if (node.configType == EConfigType.Socks) + { + ServersItem serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.method = null; + serversItem.password = null; + + if (!Utils.IsNullOrEmpty(node.security) + && !Utils.IsNullOrEmpty(node.id)) + { + SocksUsersItem socksUsersItem = new() + { + user = node.security, + pass = node.id, + level = 1 + }; + + serversItem.users = new List() { socksUsersItem }; + } + + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + + outbound.protocol = Global.socksProtocolLite; + outbound.settings.vnext = null; + } + else if (node.configType == EConfigType.VLESS) + { + VnextItem vnextItem; + if (outbound.settings.vnext.Count <= 0) + { + vnextItem = new VnextItem(); + outbound.settings.vnext.Add(vnextItem); + } + else + { + vnextItem = outbound.settings.vnext[0]; + } + vnextItem.address = node.address; + vnextItem.port = node.port; + + UsersItem usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users[0]; + } + usersItem.id = node.id; + usersItem.flow = string.Empty; + usersItem.email = Global.userEMail; + usersItem.encryption = node.security; + + //Mux + outbound.mux.enabled = _config.coreBasicItem.muxEnabled; + outbound.mux.concurrency = _config.coreBasicItem.muxEnabled ? 8 : -1; + + if (node.streamSecurity == Global.StreamSecurityReality + || node.streamSecurity == Global.StreamSecurity) + { + if (!Utils.IsNullOrEmpty(node.flow)) + { + usersItem.flow = node.flow; + + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + } + } + + outbound.protocol = Global.vlessProtocolLite; + outbound.settings.servers = null; + } + else if (node.configType == EConfigType.Trojan) + { + ServersItem serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.password = node.id; + serversItem.flow = string.Empty; + + serversItem.ota = false; + serversItem.level = 1; + + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + + outbound.protocol = Global.trojanProtocolLite; + outbound.settings.vnext = null; + } + boundStreamSettings(node, outbound.streamSettings); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int boundStreamSettings(ProfileItem node, StreamSettings streamSettings) + { + try + { + streamSettings.network = node.GetNetwork(); + string host = node.requestHost.TrimEx(); + string sni = node.sni; + string useragent = ""; + if (!_config.coreBasicItem.defUserAgent.IsNullOrEmpty()) + { + try + { + useragent = Global.userAgentTxt[_config.coreBasicItem.defUserAgent]; + } + catch (KeyNotFoundException) + { + useragent = _config.coreBasicItem.defUserAgent; + } + } + + //if tls + if (node.streamSecurity == Global.StreamSecurity) + { + streamSettings.security = node.streamSecurity; + + TlsSettings tlsSettings = new() + { + allowInsecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), + alpn = node.GetAlpn(), + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint + }; + if (!string.IsNullOrWhiteSpace(sni)) + { + tlsSettings.serverName = sni; + } + else if (!string.IsNullOrWhiteSpace(host)) + { + tlsSettings.serverName = Utils.String2List(host)[0]; + } + streamSettings.tlsSettings = tlsSettings; + } + + //if Reality + if (node.streamSecurity == Global.StreamSecurityReality) + { + streamSettings.security = node.streamSecurity; + + TlsSettings realitySettings = new() + { + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint, + serverName = sni, + publicKey = node.publicKey, + shortId = node.shortId, + spiderX = node.spiderX, + }; + + streamSettings.realitySettings = realitySettings; + } + + //streamSettings + switch (node.GetNetwork()) + { + case "kcp": + KcpSettings kcpSettings = new() + { + mtu = _config.kcpItem.mtu, + tti = _config.kcpItem.tti + }; + + kcpSettings.uplinkCapacity = _config.kcpItem.uplinkCapacity; + kcpSettings.downlinkCapacity = _config.kcpItem.downlinkCapacity; + + kcpSettings.congestion = _config.kcpItem.congestion; + kcpSettings.readBufferSize = _config.kcpItem.readBufferSize; + kcpSettings.writeBufferSize = _config.kcpItem.writeBufferSize; + kcpSettings.header = new Header + { + type = node.headerType + }; + if (!Utils.IsNullOrEmpty(node.path)) + { + kcpSettings.seed = node.path; + } + streamSettings.kcpSettings = kcpSettings; + break; + //ws + case "ws": + WsSettings wsSettings = new(); + wsSettings.headers = new Headers(); + string path = node.path; + if (!string.IsNullOrWhiteSpace(host)) + { + wsSettings.headers.Host = host; + } + if (!string.IsNullOrWhiteSpace(path)) + { + wsSettings.path = path; + } + if (!string.IsNullOrWhiteSpace(useragent)) + { + wsSettings.headers.UserAgent = useragent; + } + streamSettings.wsSettings = wsSettings; + + break; + //h2 + case "h2": + HttpSettings httpSettings = new(); + + if (!string.IsNullOrWhiteSpace(host)) + { + httpSettings.host = Utils.String2List(host); + } + httpSettings.path = node.path; + + streamSettings.httpSettings = httpSettings; + + break; + //quic + case "quic": + QuicSettings quicsettings = new() + { + security = host, + key = node.path, + header = new Header + { + type = node.headerType + } + }; + streamSettings.quicSettings = quicsettings; + if (node.streamSecurity == Global.StreamSecurity) + { + if (!string.IsNullOrWhiteSpace(sni)) + { + streamSettings.tlsSettings.serverName = sni; + } + else + { + streamSettings.tlsSettings.serverName = node.address; + } + } + break; + + case "grpc": + GrpcSettings grpcSettings = new() + { + serviceName = node.path, + multiMode = (node.headerType == Global.GrpcmultiMode), + idle_timeout = _config.grpcItem.idle_timeout, + health_check_timeout = _config.grpcItem.health_check_timeout, + permit_without_stream = _config.grpcItem.permit_without_stream, + initial_windows_size = _config.grpcItem.initial_windows_size, + }; + streamSettings.grpcSettings = grpcSettings; + break; + + default: + //tcp + if (node.headerType == Global.TcpHeaderHttp) + { + TcpSettings tcpSettings = new() + { + header = new Header + { + type = node.headerType + } + }; + + //request Host + string request = Utils.GetEmbedText(Global.v2raySampleHttprequestFileName); + string[] arrHost = host.Split(','); + string host2 = string.Join("\",\"", arrHost); + request = request.Replace("$requestHost$", $"\"{host2}\""); + //request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost())); + request = request.Replace("$requestUserAgent$", $"\"{useragent}\""); + //Path + string pathHttp = @"/"; + if (!Utils.IsNullOrEmpty(node.path)) + { + string[] arrPath = node.path.Split(','); + pathHttp = string.Join("\",\"", arrPath); + } + request = request.Replace("$requestPath$", $"\"{pathHttp}\""); + tcpSettings.header.request = Utils.FromJson(request); + + streamSettings.tcpSettings = tcpSettings; + } + break; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int dns(V2rayConfig v2rayConfig) + { + try + { + if (string.IsNullOrWhiteSpace(_config.remoteDNS)) + { + return 0; + } + + //Outbound Freedom domainStrategy + if (!string.IsNullOrWhiteSpace(_config.domainStrategy4Freedom)) + { + var outbound = v2rayConfig.outbounds[1]; + outbound.settings.domainStrategy = _config.domainStrategy4Freedom; + outbound.settings.userLevel = 0; + } + + var obj = Utils.ParseJson(_config.remoteDNS); + if (obj?.ContainsKey("servers") == true) + { + v2rayConfig.dns = obj; + } + else + { + List servers = new(); + + string[] arrDNS = _config.remoteDNS.Split(','); + foreach (string str in arrDNS) + { + //if (Utils.IsIP(str)) + //{ + servers.Add(str); + //} + } + //servers.Add("localhost"); + v2rayConfig.dns = new Mode.Dns + { + servers = servers + }; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int statistic(V2rayConfig v2rayConfig) + { + if (_config.guiItem.enableStatistics) + { + string tag = Global.InboundAPITagName; + API apiObj = new(); + Policy policyObj = new(); + SystemPolicy policySystemSetting = new(); + + string[] services = { "StatsService" }; + + v2rayConfig.stats = new Stats(); + + apiObj.tag = tag; + apiObj.services = services.ToList(); + v2rayConfig.api = apiObj; + + policySystemSetting.statsOutboundDownlink = true; + policySystemSetting.statsOutboundUplink = true; + policyObj.system = policySystemSetting; + v2rayConfig.policy = policyObj; + + if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) + { + Inbounds apiInbound = new(); + Inboundsettings apiInboundSettings = new(); + apiInbound.tag = tag; + apiInbound.listen = Global.Loopback; + apiInbound.port = Global.statePort; + apiInbound.protocol = Global.InboundAPIProtocal; + apiInboundSettings.address = Global.Loopback; + apiInbound.settings = apiInboundSettings; + v2rayConfig.inbounds.Add(apiInbound); + } + + if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) + { + RulesItem apiRoutingRule = new() + { + inboundTag = new List { tag }, + outboundTag = tag, + type = "field" + }; + v2rayConfig.routing.rules.Add(apiRoutingRule); + } + } + return 0; + } + + #region Gen speedtest config + + public string GenerateClientSpeedtestConfigString(List selecteds, out string msg) + { + try + { + if (_config == null) + { + msg = ResUI.CheckServerSettings; + return ""; + } + + msg = ResUI.InitialConfiguration; + + Config configCopy = Utils.DeepCopy(_config); + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return ""; + } + + V2rayConfig? v2rayConfig = Utils.FromJson(result); + if (v2rayConfig == null) + { + msg = ResUI.FailedGenDefaultConfiguration; + return ""; + } + List lstIpEndPoints = new(); + List lstTcpConns = new(); + try + { + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); + lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + + log(v2rayConfig); + v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. + + int httpPort = LazyConfig.Instance.GetLocalPort("speedtest"); + + foreach (var it in selecteds) + { + if (it.configType == EConfigType.Custom) + { + continue; + } + if (it.port <= 0) + { + continue; + } + if (it.configType is EConfigType.VMess or EConfigType.VLESS) + { + var item2 = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item2 is null || Utils.IsNullOrEmpty(item2.id) || !Utils.IsGuidByParse(item2.id)) + { + continue; + } + } + + //find unuse port + var port = httpPort; + for (int k = httpPort; k < Global.MaxPort; k++) + { + if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) + { + continue; + } + if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) + { + continue; + } + //found + port = k; + httpPort = port + 1; + break; + } + + //Port In Used + if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) + { + continue; + } + it.port = port; + it.allowTest = true; + + //inbound + Inbounds inbound = new() + { + listen = Global.Loopback, + port = port, + protocol = Global.InboundHttp + }; + inbound.tag = Global.InboundHttp + inbound.port.ToString(); + v2rayConfig.inbounds.Add(inbound); + + //outbound + V2rayConfig? v2rayConfigCopy = Utils.FromJson(result); + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) + { + continue; + } + if (item.configType == EConfigType.Shadowsocks + && !Global.ssSecuritysInXray.Contains(item.security)) + { + continue; + } + if (item.configType == EConfigType.VLESS + && !Global.flows.Contains(item.flow)) + { + continue; + } + + outbound(item, v2rayConfigCopy); + v2rayConfigCopy.outbounds[0].tag = Global.agentTag + inbound.port.ToString(); + v2rayConfig.outbounds.Add(v2rayConfigCopy.outbounds[0]); + + //rule + RulesItem rule = new() + { + inboundTag = new List { inbound.tag }, + outboundTag = v2rayConfigCopy.outbounds[0].tag, + type = "field" + }; + v2rayConfig.routing.rules.Add(rule); + } + + //msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); + return Utils.ToJson(v2rayConfig); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + msg = ResUI.FailedGenDefaultConfiguration; + return ""; + } + } + + #endregion Gen speedtest config + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreHandler.cs b/v2rayN/v2rayN/Handler/CoreHandler.cs index 182e9491..818cfaf2 100644 --- a/v2rayN/v2rayN/Handler/CoreHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreHandler.cs @@ -195,7 +195,7 @@ namespace v2rayN.Handler string msg = e.Data + Environment.NewLine; ShowMsg(false, msg); } - }; + }; p.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) diff --git a/v2rayN/v2rayN/Handler/MainFormHandler.cs b/v2rayN/v2rayN/Handler/MainFormHandler.cs index 916237ca..ddb4612a 100644 --- a/v2rayN/v2rayN/Handler/MainFormHandler.cs +++ b/v2rayN/v2rayN/Handler/MainFormHandler.cs @@ -144,9 +144,7 @@ namespace v2rayN.Handler { return; } - //Config configCopy = Utils.DeepCopy(config); - //configCopy.index = index; - if (CoreConfigHandler.Export2ClientConfig(item, fileName, out string msg) != 0) + if (CoreConfigHandler.GenerateClientConfig(item, fileName, out string msg, out string content) != 0) { UI.Show(msg); } diff --git a/v2rayN/v2rayN/Mode/SingboxConfig.cs b/v2rayN/v2rayN/Mode/SingboxConfig.cs new file mode 100644 index 00000000..e82b8e6d --- /dev/null +++ b/v2rayN/v2rayN/Mode/SingboxConfig.cs @@ -0,0 +1,174 @@ +namespace v2rayN.Mode +{ + public class SingboxConfig + { + public Log4Sbox log { get; set; } + public Dns4Sbox dns { get; set; } + public List inbounds { get; set; } + public List outbounds { get; set; } + public Route4Sbox route { get; set; } + public Experimental4Sbox experimental { get; set; } + } + + public class Log4Sbox + { + public bool? disabled { get; set; } + public string level { get; set; } + public string output { get; set; } + public bool timestamp { get; set; } + } + + public class Dns4Sbox + { + public List servers { get; set; } + public List rules { get; set; } + } + + public class Route4Sbox + { + public List rules { get; set; } + public bool? auto_detect_interface { get; set; } + } + + [Serializable] + public class Rule4Sbox + { + public string outbound { get; set; } + public string server { get; set; } + public bool? disable_cache { get; set; } + public List? inbound { get; set; } + public List? protocol { get; set; } + public string type { get; set; } + public string mode { get; set; } + public List? port { get; set; } + public List? port_range { get; set; } + public List? geosite { get; set; } + public List? domain { get; set; } + public List? domain_suffix { get; set; } + public List? domain_keyword { get; set; } + public List? domain_regex { get; set; } + public List? geoip { get; set; } + public List? ip_cidr { get; set; } + + public List? process_name { get; set; } + } + + [Serializable] + public class Inbound4Sbox + { + public string type { get; set; } + public string tag { get; set; } + public string listen { get; set; } + public int listen_port { get; set; } + public string domain_strategy { get; set; } + public string interface_name { get; set; } + public string inet4_address { get; set; } + public string inet6_address { get; set; } + public int? mtu { get; set; } + public bool? auto_route { get; set; } + public bool? strict_route { get; set; } + public bool? endpoint_independent_nat { get; set; } + public string? stack { get; set; } + public bool? sniff { get; set; } + public bool? sniff_override_destination { get; set; } + } + + public class Outbound4Sbox + { + public string type { get; set; } + public string tag { get; set; } + public string server { get; set; } + public int? server_port { get; set; } + public string uuid { get; set; } + public string security { get; set; } + public int? alter_id { get; set; } + public string flow { get; set; } + public int? up_mbps { get; set; } + public int? down_mbps { get; set; } + public string auth_str { get; set; } + public int? recv_window_conn { get; set; } + public int? recv_window { get; set; } + public bool? disable_mtu_discovery { get; set; } + public string detour { get; set; } + public string method { get; set; } + public string username { get; set; } + public string password { get; set; } + public string? version { get; set; } + public string? network { get; set; } + public string packet_encoding { get; set; } + public Tls4Sbox tls { get; set; } + public Multiplex4Sbox multiplex { get; set; } + public Transport4Sbox transport { get; set; } + } + + public class Tls4Sbox + { + public bool enabled { get; set; } + public string server_name { get; set; } + public bool? insecure { get; set; } + public List alpn { get; set; } + public Utls4Sbox utls { get; set; } + public Reality4Sbox reality { get; set; } + } + + public class Multiplex4Sbox + { + public bool enabled { get; set; } + public string protocol { get; set; } + public int max_connections { get; set; } + public int min_streams { get; set; } + public int max_streams { get; set; } + } + + public class Utls4Sbox + { + public bool enabled { get; set; } + public string fingerprint { get; set; } + } + + public class Reality4Sbox + { + public bool enabled { get; set; } + public string public_key { get; set; } + public string short_id { get; set; } + } + + public class Transport4Sbox + { + public string type { get; set; } + public List? host { get; set; } + public string? path { get; set; } + public string service_name { get; set; } + public string idle_timeout { get; set; } + public string ping_timeout { get; set; } + public bool? permit_without_stream { get; set; } + } + + public class Server4Sbox + { + public string tag { get; set; } + public string address { get; set; } + public string address_resolver { get; set; } + public string strategy { get; set; } + public string detour { get; set; } + } + + public class Experimental4Sbox + { + public V2ray_Api4Sbox v2ray_api { get; set; } + } + + public class V2ray_Api4Sbox + { + public string listen { get; set; } + public Stats4Sbox stats { get; set; } + } + + public class Stats4Sbox + { + public bool enabled { get; set; } + public List? inbounds { get; set; } + public List? outbounds { get; set; } + public List? users { get; set; } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Sample/SampleServerConfig b/v2rayN/v2rayN/Sample/SampleServerConfig deleted file mode 100644 index 3aa0bb76..00000000 --- a/v2rayN/v2rayN/Sample/SampleServerConfig +++ /dev/null @@ -1,33 +0,0 @@ -{ - "log": { - "access": "/var/log/v2ray/access.log", - "error": "/var/log/v2ray/error.log", - "loglevel": "warning" - }, - "inbounds": [{ - "port": 10086, - "protocol": "vmess", - "settings": { - "clients": [{ - "id": "23ad6b10-8d1a-40f7-8ad0-e3e35cd38297", - "level": 1, - "email": "t@t.tt" - }] - }, - "streamSettings": { - "network": "tcp" - } - }], - "outbounds": [{ - "protocol": "freedom", - "settings": {} - }, { - "protocol": "blackhole", - "settings": {}, - "tag": "block" - }], - "routing": { - "domainStrategy": "IPIfNonMatch", - "rules": [] - } -} \ No newline at end of file diff --git a/v2rayN/v2rayN/Sample/SingboxSampleClientConfig b/v2rayN/v2rayN/Sample/SingboxSampleClientConfig new file mode 100644 index 00000000..ce3d9305 --- /dev/null +++ b/v2rayN/v2rayN/Sample/SingboxSampleClientConfig @@ -0,0 +1,33 @@ +{ + "log": { + "level": "debug", + "timestamp": true + }, + "inbounds": [ + { + "type": "socks", + "tag": "socks", + "listen": "127.0.0.1", + "listen_port": 10000 + } + ], + "outbounds": [ + { + "type": "vless", + "tag": "proxy", + "server": "", + "server_port": 443 + }, + { + "type": "direct", + "tag": "direct" + }, + { + "type": "block", + "tag": "block" + } + ], + "route": { + "rules": [] + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/v2rayN.csproj b/v2rayN/v2rayN/v2rayN.csproj index 2f3b287c..eeb85117 100644 --- a/v2rayN/v2rayN/v2rayN.csproj +++ b/v2rayN/v2rayN/v2rayN.csproj @@ -12,7 +12,7 @@ Copyright © 2017-2023 (GPLv3) 6.23 - + @@ -31,6 +31,9 @@ + + Never + Never @@ -64,9 +67,6 @@ Never - - Never - Never