diff --git a/LICENSE b/LICENSE index 94a9ed02..bfbc868f 100644 --- a/LICENSE +++ b/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Copyright (C) + Copyright (C) 2019-Present 2dust This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Copyright (C) + v2rayN Copyright (C) 2019-Present 2dust This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index 1cf9ee45..1c597d14 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # v2rayN -A GUI client for Windows, Linux and macOS, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/cg3s/v2rayN/wiki/List-of-supported-cores) +A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core) +and [sing-box](https://github.com/SagerNet/sing-box) +and [others](https://github.com/cg3s/v2rayN/wiki/List-of-supported-cores) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/cg3s/v2rayN)](https://github.com/cg3s/v2rayN/commits/master) [![CodeFactor](https://www.codefactor.io/repository/github/cg3s/v2rayn/badge)](https://www.codefactor.io/repository/github/cg3s/v2rayn) diff --git a/pkg2appimage.yml b/pkg2appimage.yml index 0b4565c7..ab52666e 100644 --- a/pkg2appimage.yml +++ b/pkg2appimage.yml @@ -4,7 +4,7 @@ binpatch: true ingredients: script: - export FileName="v2rayN-${AppImageOutputArch}.zip" - - wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/${FileName}" + - wget -nv -O $FileName "https://github.com/cg3s/v2rayN-core-bin/raw/refs/heads/master/${FileName}" - 7z x $FileName -aoa - cp -rf v2rayN-${AppImageOutputArch}/* $OutputPath diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index 8dcfcaaa..78438651 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.11.3 + 7.12.5 diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 5777d1bb..e9c8d492 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -5,10 +5,10 @@ false - - - - + + + + diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 05f30dfe..bd2e8863 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -8,9 +8,7 @@ public class Global public const string GithubUrl = "https://github.com"; public const string GithubApiUrl = "https://api.github.com/repos"; public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; - public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; - public const string IPAPIUrl = "https://api.ip.sb/geoip"; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string ConfigFileName = "guiNConfig.json"; @@ -519,5 +517,15 @@ public class Global @"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" ]; + public static readonly List IPAPIUrls = + [ + @"https://speed.cloudflare.com/meta", + @"https://api.ip.sb/geoip", + @"https://api-ipv4.ip.sb/geoip", + @"https://api-ipv6.ip.sb/geoip", + @"https://api.ipapi.is", + @"" + ]; + #endregion const } diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index 906e2ba3..ad9a4029 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -9,7 +9,6 @@ public sealed class AppHandler private int? _statePort; private int? _statePort2; private Job? _processJob; - private bool? _isAdministrator; public static AppHandler Instance => _instance.Value; public Config Config => _config; diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index a23b1c67..6a25b56f 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -122,7 +122,7 @@ public class ConfigHandler } if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty()) { - config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; + config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrls.First(); } if (config.SpeedTestItem.MixedConcurrencyCount < 1) { @@ -799,8 +799,11 @@ public class ConfigHandler { return -1; } + var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? []; var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); var lstProfile = (from t in lstModel + join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b + from t22 in t2b.DefaultIfEmpty() join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b from t33 in t3b.DefaultIfEmpty() select new ProfileItemModel @@ -815,7 +818,11 @@ public class ConfigHandler StreamSecurity = t.StreamSecurity, Delay = t33?.Delay ?? 0, Speed = t33?.Speed ?? 0, - Sort = t33?.Sort ?? 0 + Sort = t33?.Sort ?? 0, + TodayDown = (t22?.TodayDown ?? 0).ToString("D16"), + TodayUp = (t22?.TodayUp ?? 0).ToString("D16"), + TotalDown = (t22?.TotalDown ?? 0).ToString("D16"), + TotalUp = (t22?.TotalUp ?? 0).ToString("D16"), }).ToList(); Enum.TryParse(colName, true, out EServerColName name); @@ -833,6 +840,10 @@ public class ConfigHandler EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(), EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(), EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(), + EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(), + EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(), + EServerColName.TotalDown => lstProfile.OrderBy(t => t.TotalDown).ToList(), + EServerColName.TotalUp => lstProfile.OrderBy(t => t.TotalUp).ToList(), _ => lstProfile }; } @@ -849,6 +860,10 @@ public class ConfigHandler EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(), EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(), EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(), + EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(), + EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(), + EServerColName.TotalDown => lstProfile.OrderByDescending(t => t.TotalDown).ToList(), + EServerColName.TotalUp => lstProfile.OrderByDescending(t => t.TotalUp).ToList(), _ => lstProfile }; } diff --git a/v2rayN/ServiceLib/Handler/ConnectionHandler.cs b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs new file mode 100644 index 00000000..3cfc6020 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs @@ -0,0 +1,42 @@ +namespace ServiceLib.Handler; + +public class ConnectionHandler +{ + private static readonly Lazy _instance = new(() => new()); + public static ConnectionHandler Instance => _instance.Value; + + public async Task RunAvailabilityCheck() + { + var downloadHandle = new DownloadService(); + var time = await downloadHandle.RunAvailabilityCheck(null); + var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None; + + return string.Format(ResUI.TestMeOutput, time, ip); + } + + private async Task GetIPInfo(DownloadService downloadHandle) + { + var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl; + if (url.IsNullOrEmpty()) + { + return null; + } + + var result = await downloadHandle.TryDownloadString(url, true, ""); + if (result == null) + { + return null; + } + + var ipInfo = JsonUtils.Deserialize(result); + if (ipInfo == null) + { + return null; + } + + var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query; + var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code; + + return $"({country ?? "unknown"}) {ip}"; + } +} diff --git a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs index 85d90fc3..7fd07262 100644 --- a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs @@ -10,7 +10,6 @@ public class CoreAdminHandler public static CoreAdminHandler Instance => _instance.Value; private Config _config; private Action? _updateFunc; - private const string _tag = "CoreAdminHandler"; private int _linuxSudoPid = -1; public async Task Init(Config config, Action updateFunc) @@ -30,69 +29,60 @@ public class CoreAdminHandler public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) { - try + var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; + var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); + + Process proc = new() { - var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); - - Process proc = new() + StartInfo = new() { - StartInfo = new() - { - FileName = shFilePath, - Arguments = "", - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardInputEncoding = Encoding.UTF8, - StandardOutputEncoding = Encoding.UTF8, - StandardErrorEncoding = Encoding.UTF8, - } - }; - - proc.OutputDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - proc.ErrorDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - - proc.Start(); - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(); - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); - - await Task.Delay(100); - if (proc is null or { HasExited: true }) - { - throw new Exception(ResUI.FailedToRunCore); + FileName = shFilePath, + Arguments = "", + WorkingDirectory = Utils.GetBinConfigPath(), + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardInputEncoding = Encoding.UTF8, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, } + }; - _linuxSudoPid = proc.Id; - - return proc; - } - catch (Exception ex) + proc.OutputDataReceived += (sender, e) => { - Logging.SaveLog(_tag, ex); - UpdateFunc(false, ex.Message); - return null; + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + proc.ErrorDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(); + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); + + await Task.Delay(100); + if (proc is null or { HasExited: true }) + { + throw new Exception(ResUI.FailedToRunCore); } + + _linuxSudoPid = proc.Id; + + return proc; } public async Task KillProcessAsLinuxSudo() @@ -102,22 +92,14 @@ public class CoreAdminHandler return; } - try - { - var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); + var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; + var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); - var result = await Cli.Wrap(shFilePath) - .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) - .ExecuteAsync(); + await Cli.Wrap(shFilePath) + .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) + .ExecuteAsync(); - _linuxSudoPid = -1; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - UpdateFunc(false, ex.Message); - } + _linuxSudoPid = -1; } private async Task CreateLinuxShellFile(string cmdLine, string fileName) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 97918693..92c30171 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -238,66 +238,19 @@ public class CoreHandler return null; } - if (mayNeedSudo - && _config.TunModeItem.EnableTun - && coreInfo.CoreType == ECoreType.sing_box - && Utils.IsNonWindows()) - { - _linuxSudo = true; - await CoreAdminHandler.Instance.Init(_config, _updateFunc); - return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); - } - try { - Process proc = new() + if (mayNeedSudo + && _config.TunModeItem.EnableTun + && coreInfo.CoreType == ECoreType.sing_box + && Utils.IsNonWindows()) { - StartInfo = new() - { - FileName = fileName, - Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardOutput = displayLog, - RedirectStandardError = displayLog, - CreateNoWindow = true, - StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, - StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, - } - }; - - if (displayLog) - { - proc.OutputDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - proc.ErrorDataReceived += (sender, e) => - { - if (e.Data.IsNotEmpty()) - { - UpdateFunc(false, e.Data + Environment.NewLine); - } - }; - } - proc.Start(); - - if (displayLog) - { - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); + _linuxSudo = true; + await CoreAdminHandler.Instance.Init(_config, _updateFunc); + return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); } - await Task.Delay(500); - AppHandler.Instance.AddProcess(proc.Handle); - if (proc is null or { HasExited: true }) - { - throw new Exception(ResUI.FailedToRunCore); - } - return proc; + return await RunProcessNormal(fileName, coreInfo, configPath, displayLog); } catch (Exception ex) { @@ -307,5 +260,57 @@ public class CoreHandler } } + private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog) + { + Process proc = new() + { + StartInfo = new() + { + FileName = fileName, + Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), + WorkingDirectory = Utils.GetBinConfigPath(), + UseShellExecute = false, + RedirectStandardOutput = displayLog, + RedirectStandardError = displayLog, + CreateNoWindow = true, + StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, + StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, + } + }; + + if (displayLog) + { + proc.OutputDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + proc.ErrorDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + } + proc.Start(); + + if (displayLog) + { + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + } + + await Task.Delay(100); + AppHandler.Instance.AddProcess(proc.Handle); + if (proc is null or { HasExited: true }) + { + throw new Exception(ResUI.FailedToRunCore); + } + return proc; + } + #endregion Process } diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index 71b4c0c2..5892236f 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -6,9 +6,9 @@ public class VmessFmt : BaseFmt { msg = ResUI.ConfigurationFormatIncorrect; ProfileItem? item; - if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0) + if (str.IndexOf('@') > 0) { - item = ResolveStdVmess(str); + item = ResolveStdVmess(str) ?? ResolveVmess(str, out msg); } else { diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 47c96a3f..afd7e7cc 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -156,6 +156,7 @@ public class SpeedTestItem public string SpeedTestUrl { get; set; } public string SpeedPingTestUrl { get; set; } public int MixedConcurrencyCount { get; set; } + public string IPAPIUrl { get; set; } } [Serializable] diff --git a/v2rayN/ServiceLib/Models/IPAPIInfo.cs b/v2rayN/ServiceLib/Models/IPAPIInfo.cs index 21063946..7ba16727 100644 --- a/v2rayN/ServiceLib/Models/IPAPIInfo.cs +++ b/v2rayN/ServiceLib/Models/IPAPIInfo.cs @@ -3,10 +3,17 @@ namespace ServiceLib.Models; internal class IPAPIInfo { public string? ip { get; set; } - public string? city { get; set; } - public string? region { get; set; } - public string? region_code { get; set; } + public string? clientIp { get; set; } + public string? ip_addr { get; set; } + public string? query { get; set; } public string? country { get; set; } public string? country_name { get; set; } public string? country_code { get; set; } + public string? countryCode { get; set; } + public LocationInfo? location { get; set; } +} + +public class LocationInfo +{ + public string? country_code { get; set; } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 6a9a3ae3..648a633a 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3192,6 +3192,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Current connection info test URL 的本地化字符串。 + /// + public static string TbSettingsIPAPIUrl { + get { + return ResourceManager.GetString("TbSettingsIPAPIUrl", resourceCulture); + } + } + /// /// 查找类似 Keep older entries when de-duplicating 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 5463565a..cbe269d8 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1413,4 +1413,7 @@ صادر کردن سرور + + URL آزمایش اطلاعات اتصال فعلی + diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 130c3579..4f8824b7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1416,4 +1416,7 @@ Export server + + Current connection info test URL + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 7a6bc893..5d42790f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1413,4 +1413,7 @@ Export Configuration + + Current connection info test URL + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 53515f39..a6747246 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1,17 +1,17 @@ - @@ -118,16 +118,16 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Экспортирование URL в буфер обмена успешно завершено + Ссылка успешно скопирована в буфер обмена - Пожалуйста, сначала проверьте настройки сервера + Сначала проверьте настройки сервера Недопустимый формат конфигурации - Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, пожалуйста, измените порт прослушивания вручную. + Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, то измените порт прослушивания вручную Загрузка... @@ -136,13 +136,13 @@ Скорость загрузки - Хотите загрузить? {0} + Хотите загрузить {0}? Не удалось преобразовать файл конфигурации - Не удалось создать файл конфигурации по умолчаниюe + Не удалось создать файл конфигурации по умолчанию Не удалось получить конфигурацию по умолчанию @@ -154,37 +154,37 @@ Не удалось прочитать файл конфигурации - Пожалуйста, укажите порт сервера в правильном формате + Укажите порт сервера в правильном формате - Пожалуйста, укажите локальный порт прослушивания + Введите локальный порт для прослушивания - Пожалуйста, введите пароль + Введите пароль - Пожалуйста, заполните адрес сервера + Введите адрес сервера - Пожалуйста, заполните идентификатор пользователя + Введите идентификатор пользователя - Некорректная конфигурация, пожалуйста, проверьте + Некорректная конфигурация. Проверьте Исходная конфигурация - {0} {1} является последней версией. + {0} {1} является последней версией - {0} {1} является последней версией. + {0} {1} является последней версией Адрес - Безопасность + Метод шифрования Порт @@ -199,7 +199,7 @@ Загружено трафика сегодня - Отдано трафика сегодня + Отправлено сегодня Всего загружено @@ -214,13 +214,13 @@ Ядро успешно загружено - Не удалось импортировать содержимое подписки + Не удалось импортировать подписку Содержимое подписки успешно импортировано - Нет установлены подписки + Нет установленных подписок Парсинг {0} прошел успешно @@ -235,7 +235,7 @@ Некорректное содержимое подписки - распаковывается... + Распаковка… Обновление подписки закончено @@ -244,37 +244,37 @@ Обновление подписки начинается - Успешное обновление ядра + Ядро успешно обновлено Успешное обновление ядра! Перезапуск службы... - Не является протоколом Vmess или SS + Не является протоколом VMess или Shadowsocks - Файл Core (имя файла: {1}) не найден в папке ({0}), загрузите и поместите его в папку, адрес загрузки: {2} + Файл ядра ({1}) не найден в папке {0}. Скачайте по адресу {2} и поместите его туда Сканирование завершено, не найден корректный QR код - операция безуспешна, проверьте и попробуйте ещё раз + Операция безуспешна, проверьте и попробуйте ещё раз - Пожалуйста, заполните примечания + Введите примечания - Пожалуйста, выберите метод шифрования + Выберите метод шифрования - Пожалуйста, выберите протокол + Выберите протокол Сначала выберите сервер - Удаление дублей завершено. Старая: {0}, Новая: {1}. + Удаление дублей завершено. Старая: {0}, Новая: {1} Вы уверены, что хотите удалить сервер? @@ -286,16 +286,16 @@ Запуск сервиса ({0})... - Конфигурация успешна {0} + Конфигурация выполнена успешно {0} - Пользовательский сервер конфигурации успешно импортирован. + Пользовательская конфигурация сервера успешно импортирована - {0} серверов импортировано из буфера обмена. + {0} серверов импортировано из буфера обмена - Сканирование URL-адреса импорта успешна. + Сканирование URL-адреса импорта прошло успешно Задержка текущего сервера: {0} мс, {1} @@ -310,7 +310,7 @@ Вы уверены, что хотите удалить правила? - {0}, Один из обязательных.. + {0}: одно из обязательных полей Примечания @@ -322,13 +322,13 @@ Количество - Пожалуйста, заполните адрес (Url) + Введите URL-адрес - Вы хотите добавить правила? Выберите «Да», чтобы добавить, выберите иное, чтобы заменить + Хотите добавить правила? Выберите «Да» для добавления или «Нет» для замены - Загрузка GeoFile: {0} успешна + Загрузка GeoFile {0} прошла успешно Информация @@ -337,49 +337,49 @@ Пользовательская иконка - Пожалуйста, заполните правильный пользовательский DNS + Введите корректный пользовательский DNS - *ws путь + *WebSocket-путь - *h2 путь + *HTTP2-путь - *QUIC ключ/Kcp seed + *QUIC-ключ / KCP-seed - *grpc serviceName + Имя сервиса *gRPC - *http хосты разделенные запятыми (,) + *http-хосты, разделённые запятыми (,) - *ws хост + *WebSocket-хост - *h2 хосты разделенные запятыми (,) + *HTTP2-хосты, разделённые запятыми (,) - * безопасность QUIC + Безопасность *QUIC - * тип TCP камуфляжа + Тип *TCP-камуфляжа - * тип KCP камуфляжа + Тип *KCP-камуфляжа - * тип QUIC камуфляжа + Тип *QUIC-камуфляжа - * режим grpc + Режим *gRPC TLS - *Kcp seed + *KCP-seed Не удалось зарегистрировать глобальную горячую клавишу {0}, причина: {1} @@ -391,10 +391,10 @@ Разгруппировано - Все сервера + Все серверы - Пожалуйста, просмотрите, чтобы импортировать конфигурацию сервера + Выберите файл конфигурации сервера для импорта Тестирование... @@ -433,7 +433,7 @@ Настройки маршрутизации - Сервера + Серверы Настройки @@ -442,7 +442,7 @@ Обновить текущую подписку без прокси - Обновить текущую подписку с прокси + Обновить подписку через прокси Группа подписки @@ -568,7 +568,7 @@ Сортировка - User Agent + Заголовок User-Agent Отмена @@ -625,7 +625,7 @@ TLS - * По-умолчанию TCP + *По-умолчанию TCP Ядро @@ -655,10 +655,10 @@ Шифрования - txtPreSocksPort + Порт SOCKS - * После установки этого значения служба socks будет запущена с использованием Xray/sing-box(Tun) для обеспечения таких функций, как отображение скорости + * После установки этого значения служба SOCKS будет запущена с использованием Xray/sing-box(TUN) для обеспечения таких функций, как отображение скорости Просмотр @@ -694,7 +694,7 @@ Разрешить небезопасные - Outbound Freedom domainStrategy + «Freedom»: стратегия обработки доменов исходящего трафика Автоматически регулировать ширину столбца после обновления подписки @@ -709,7 +709,7 @@ Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;) - Display real-time speed + Показывать скорость в реальном времени Сохранить старые при удалении дублей @@ -718,10 +718,10 @@ Записывать логи - Уровень логгирования + Уровень записи логов - Включите мультиплексирование Mux + Включить мультиплексирование Mux Настройки v2rayN @@ -730,16 +730,16 @@ Пароль аутентификации - Пользовательский DNS (если несколько делите с запятыми (,)) + Пользовательский DNS (если несколько, то делите запятыми (,)) - Set Win10 UWP Loopback + Разрешить loopback для приложений UWP (Win10) Включить сниффинг - Mixed Port + Смешанный порт Автозапуск @@ -748,7 +748,7 @@ Включить статистику (требуется перезагрузка) - URL-адрес конверсии подписки + URL конвертации подписок Настройки системного прокси @@ -763,7 +763,7 @@ Включить UDP - Auth user + Имя пользователя (логин) Очистить системный прокси @@ -813,6 +813,9 @@ Вверх (U) + + Переместить вверх/вниз + Фильтр, поддерживает regex @@ -847,13 +850,13 @@ 2.Прямой домен или IP - 1.Прокси-домен или IP + 1.Прокси домен или IP Предустановленный список наборов правил - * Установите правила, разделенные запятыми (,); Запятая в регулярке заменена на <COMMA> + *Разделяйте правила запятыми (,). Литерал «,» — <COMMA>. Префикс # — отключить правило Импорт правил из буфера обмена @@ -880,16 +883,16 @@ Удалить правила (Delete) - RoutingRuleDetailsSetting + Детальные настройки правил маршрутизации Домен и IP автоматически сортируются при сохранении - Ruleobject Doc + Документация RuleObject - Поддержка DnsObject + Поддерживаются DNS-объекты, нажмите для просмотра документации Необязательное поле @@ -910,16 +913,16 @@ Тест задержки и скорости всех серверов (Ctrl+E) - Задержка (ms) + Задержка (мс) - Скорость (M/s) + Скорость (МБ/с) Не удалось запустить ядро, посмотрите логи - Remarks regular filter + Фильтр примечаний (Regex) Показать логи @@ -931,7 +934,7 @@ Новый порт для локальной сети - Настройки TunMode + Настройки режима TUN Перейти в группу @@ -955,22 +958,22 @@ Тест завершен - TLS отпечаток по умлочанию + TLS отпечаток по умолчанию User-Agent - Этот параметр действителен только для TCP/HTTP и WS + Параметр действует только для TCP/HTTP и WebSocket (WS) Шрифт (требуется перезагрузка) - Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки + Скопируйте файл шрифта TTF/TTC в каталог guiFonts и заново откройте окно настроек - Pac порт = +3; Xray API порт = +4; mihomo API порт = +5; + Pac порт = +3,Xray API порт = +4, mihomo API порт = +5 Установите это с правами администратора @@ -979,13 +982,10 @@ Размер шрифта - Таймаут одиночного спидтеста + Тайм-аут одиночного тестирования скорости - URL спидтеста - - - Move up and down + URL для тестирования скорости PublicKey @@ -1000,19 +1000,19 @@ Включить аппаратное ускорение (требуется перезагрузка) - Ожидание тестирования (нажмите ESC для отмены)... + Ожидание тестирования (нажмите ESC для отмены)… - Please turn off when there is an abnormal disconnection + Отключите при аномальном разрыве соединения - Updates are not enabled, skip this subscription + Обновления не включены — подписка пропущена Перезагрузить как администратор - More URLs, separated by commas; Subscription conversion will be invalid + Дополнительные URL через запятую, конвертация подписки недоступна {0} : {1}/s↑ | {2}/s↓ @@ -1021,13 +1021,13 @@ Интервал автоматического обновления в минутах - Включить логгирование в файл + Включить запись логов в файл Преобразовать тип цели - Если преобразование не требуется, оставьте поле пустым. + Если преобразование не требуется, оставьте поле пустым Настройки DNS @@ -1036,7 +1036,7 @@ Настройки DNS sing-box - Пожалуйста, заполните структуру DNS. Нажмите, чтобы просмотреть документ. + Заполните структуру DNS, нажмите, чтобы открыть документ Нажмите, чтобы импортировать конфигурацию DNS по умолчанию @@ -1045,88 +1045,88 @@ Стратегия домена для sing-box - sing-box Mux Protocol + Протокол Mux для sing-box - Full process name (Tun mode) + Полное имя процесса (режим TUN) - IP or IP CIDR + IP-адрес или сеть CIDR Домен - Добавить [Hysteria2] сервер + Добавить сервер [Hysteria2] - Hysteria Max bandwidth (Up/Dw) + Максимальная пропускная способность Hysteria (загрузка/отдача) Использовать системные узлы - Добавить [TUIC] сервер + Добавить сервер [TUIC] Контроль перегрузок - Previous proxy remarks + Примечания к предыдущему прокси - Next proxy remarks + Примечания к следующему прокси - Пожалуйста, убедитесь, что примечание существует и является уникальным. + Убедитесь, что примечание существует и является уникальным - Enable additional Inbound + Включить дополнительный входящий канал Включить IPv6 адреса - Добавить [WireGuard] сервер + Добавить сервер [WireGuard] - PrivateKey + Приватный ключ - Reserved(2,3,4) + Зарезервировано (2, 3, 4) - Адрес(Ipv4,Ipv6) + Адрес (Ipv4,Ipv6) - obfs password + Пароль obfs - (Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag + (Домен или IP или имя процесса) и порт, и протокол, и InboundTag => OutboundTag Автоматическая прокрутка в конец - URL-адрес для проверки скорости пинга + URL для быстрой проверки реальной задержки - Updating subscription, only determine remarks exists + Обновляя подписку, проверять лишь наличие примечаний Отмена тестирования... - *grpc Authority + *gRPC Authority - Добавить [HTTP] сервер + Добавить сервер [HTTP] - Use Xray and enable non-Tun mode, which conflicts with the group previous proxy + Используйте Xray и отключите режим TUN, так как он конфликтует с предыдущим прокси-сервером группы - Включить фрагмент + Включить фрагментацию (Fragment) Включить файл кэша для sing-box (файлы наборов правил) @@ -1135,7 +1135,7 @@ Пользовательский набор правил для sing-box - Successful operation. Click the settings menu to reboot the app. + Операция успешна. Перезапустите приложение Открыть место хранения @@ -1144,7 +1144,7 @@ Сортировка - Chain + Цепочка По умолчанию @@ -1156,7 +1156,7 @@ Скорость загрузки - Download Traffic + Скачанный трафик Узел @@ -1174,10 +1174,10 @@ Тип - Скорость загрузки + Скорость отдачи - Upload Traffic + Отправленный трафик Соединения @@ -1195,13 +1195,13 @@ Режим правила - Direct + Прямое соединение - Global + Глобальный режим - Do not change + Не менять Правила @@ -1210,13 +1210,13 @@ Тест задержки - Part Node Latency Test + Тест задержки выбранных узлов Обновить прокси - Активировать узел (Enter) + Сделать узел активным (Enter) Стратегия домена по умолчанию для исходящих @@ -1231,7 +1231,7 @@ Автоматическая регулировка ширины столбца - Export Base64-encoded Share Links to Clipboard + Экспорт ссылок в формате Base64 в буфер обмена Экспортировать выбранный сервер для полной конфигурации в буфер обмена @@ -1240,7 +1240,7 @@ Показать или скрыть главное окно - Пользовательская конфигурация порта socks + Пользовательская конфигурация порта SOCKS Резервное копирование и восстановление @@ -1252,28 +1252,28 @@ Восстановить из файла - Backup to remote (WebDAV) + Резервное копирование на удалённый сервер (WebDAV) - Restore from remote (WebDAV) + Восстановить с удалённого сервера (WebDAV) - Local + Локально - Remote (WebDAV) + Удалённо (WebDAV) - WebDav Url + URL WebDAV - WebDav User Name + Имя пользователя WebDAV - WebDav Password + Пароль WebDAV - WebDav Check + Проверить WebDAV Имя удаленной папки (необязательно) @@ -1282,16 +1282,16 @@ Неверный файл резервной копии - Host filter + Фильтр хостов - Active + Активный - Источник geo файлов + Источник файлов Geo (необязательно) - Источник sing-box srs файлов + Источник файлов наборов правил sing-box (необязательно) Программы для обновления не существует @@ -1312,7 +1312,7 @@ Иран - Используйте Настройки -> Региональные пресеты вместо изменения этого поля + Пользователи из Китая могут пропустить этот пункт Сканировать QR-код с изображения @@ -1321,7 +1321,7 @@ Неверный адрес (Url) - Пожалуйста, не используйте небезопасный адрес подписки по протоколу HTTP. + Не используйте небезопасный адрес подписки по протоколу HTTP Установите шрифт в систему и перезапустите настройки @@ -1330,43 +1330,43 @@ Вы уверены, что хотите выйти? - Remarks Memo + Заметка (Memo) - System sudo password + Пароль sudo системы - The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart. + Введенный вами пароль не может быть подтвержден, поэтому убедитесь, что вы ввели его правильно. Если приложение не работает должным образом из-за неправильного ввода, то перезапустите его. Пароль не будет сохранен, и вам придется вводить его заново после каждого перезапуска - Please set the sudo password in Tun mode settings first + Сначала задайте пароль sudo в настройках TUN-режима - Пожалуйста, не запускайте программу как суперпользователь. + Не запускайте программу как суперпользователь - *xhttp mode + *XHTTP-режим - XHTTP Extra raw JSON, format: { XHTTPObject } + Дополнительный XHTTP сырой JSON, формат: { XHTTPObject } Скрыть в трее при закрытии окна - The number of concurrent during multi-test + Количество одновременно выполняемых тестов при многоэтапном тестировании Исключение. Не используйте прокси-сервер для адресов с запятой (,) - Sniffing type + Тип сниффинга - Enable second mixed port + Включить второй смешанный порт - socks: local port, socks2: second local port, socks3: LAN port + socks: локальный порт, socks2: второй локальный порт, socks3: LAN порт Темы @@ -1375,7 +1375,7 @@ Копировать команду прокси в буфер обмена - Starting retesting failed parts, {0} remaining. Press ESC to terminate... + Повторное тестирование неудачных элементов, осталось {0}. Нажмите ESC для остановки… По результату теста @@ -1384,33 +1384,36 @@ Удалить недействительные по результатам теста - Удалено {0} недействительных. + Удалено {0} недействительных Диапазон портов сервера - Will cover the port, separate with commas (,) + Заменит указанный порт, перечисляйте через запятую (,) - Multi-server to custom configuration + От мультиконфигурации к пользовательской конфигурации - Multi-server Random by Xray + Случайный (Xray) - Multi-server RoundRobin by Xray + Круговой (Xray) - Multi-server LeastPing by Xray + Минимальное RTT (минимальное время туда-обратно) (Xray) - Multi-server LeastLoad by Xray + Минимальная нагрузка (Xray) - Multi-server LeastPing by sing-box + Минимальное RTT (минимальное время туда-обратно) (sing-box) - Export server + Экспортировать конфигурацию - \ No newline at end of file + + URL для тестирования текущего соединения + + diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index a712ba12..42adf7c5 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1410,4 +1410,7 @@ 导出配置文件 + + 当前连接信息测试地址 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index ae3dd18e..61d77f9f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1410,4 +1410,7 @@ 匯出設定檔 + + 目前連接資訊測試地址 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Sample/dns_v2ray_normal b/v2rayN/ServiceLib/Sample/dns_v2ray_normal index 3f9c8b22..fdb30dae 100644 --- a/v2rayN/ServiceLib/Sample/dns_v2ray_normal +++ b/v2rayN/ServiceLib/Sample/dns_v2ray_normal @@ -6,11 +6,10 @@ "servers": [ { "address": "1.1.1.1", + "skipFallback": true, "domains": [ - "geosite:geolocation-!cn" - ], - "expectIPs": [ - "geoip:!cn" + "domain:googleapis.cn", + "domain:gstatic.com" ] }, { @@ -23,6 +22,7 @@ "geoip:cn" ] }, + "1.1.1.1", "8.8.8.8", "https://dns.google/dns-query" ] diff --git a/v2rayN/ServiceLib/Sample/proxy_set_linux_sh b/v2rayN/ServiceLib/Sample/proxy_set_linux_sh index 6267fd35..335cce0e 100644 --- a/v2rayN/ServiceLib/Sample/proxy_set_linux_sh +++ b/v2rayN/ServiceLib/Sample/proxy_set_linux_sh @@ -95,6 +95,21 @@ detect_desktop_environment() { return fi + if [[ "$XDG_CURRENT_DESKTOP" == *"UKUI"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"ukui"* ]]; then + echo "gnome" + return + fi + + if [[ "$XDG_CURRENT_DESKTOP" == *"DDE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"dde"* ]]; then + echo "gnome" + return + fi + + if [[ "$XDG_CURRENT_DESKTOP" == *"MATE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"mate"* ]]; then + echo "gnome" + return + fi + local KDE_ENVIRONMENTS=("KDE" "plasma") for ENV in "${KDE_ENVIRONMENTS[@]}"; do if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 34ae6f79..27ed39b9 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -979,26 +979,6 @@ public class CoreConfigSingboxService try { var dnsOutbound = "dns_out"; - if (!_config.Inbound.First().SniffingEnabled) - { - singboxConfig.route.rules.Add(new() - { - port = [53], - network = ["udp"], - outbound = dnsOutbound - }); - } - - singboxConfig.route.rules.Insert(0, new() - { - outbound = Global.DirectTag, - clash_mode = ERuleMode.Direct.ToString() - }); - singboxConfig.route.rules.Insert(0, new() - { - outbound = Global.ProxyTag, - clash_mode = ERuleMode.Global.ToString() - }); if (_config.TunModeItem.EnableTun) { @@ -1025,6 +1005,27 @@ public class CoreConfigSingboxService }); } + if (!_config.Inbound.First().SniffingEnabled) + { + singboxConfig.route.rules.Add(new() + { + port = [53], + network = ["udp"], + outbound = dnsOutbound + }); + } + + singboxConfig.route.rules.Add(new() + { + outbound = Global.DirectTag, + clash_mode = ERuleMode.Direct.ToString() + }); + singboxConfig.route.rules.Add(new() + { + outbound = Global.ProxyTag, + clash_mode = ERuleMode.Global.ToString() + }); + var routing = await ConfigHandler.GetDefaultRouting(_config); if (routing != null) { @@ -1047,28 +1048,30 @@ public class CoreConfigSingboxService private void GenRoutingDirectExe(out List lstDnsExe, out List lstDirectExe) { - lstDnsExe = new(); - lstDirectExe = new(); - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); - foreach (var it in coreInfo) + var dnsExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); + var directExeSet = new HashSet(StringComparer.OrdinalIgnoreCase); + + var coreInfoResult = CoreInfoHandler.Instance.GetCoreInfo(); + + foreach (var coreConfig in coreInfoResult) { - if (it.CoreType == ECoreType.v2rayN) + if (coreConfig.CoreType == ECoreType.v2rayN) { continue; } - foreach (var it2 in it.CoreExes) - { - if (!lstDnsExe.Contains(it2) && it.CoreType != ECoreType.sing_box) - { - lstDnsExe.Add($"{it2}.exe"); - } - if (!lstDirectExe.Contains(it2)) + foreach (var baseExeName in coreConfig.CoreExes) + { + if (coreConfig.CoreType != ECoreType.sing_box) { - lstDirectExe.Add($"{it2}.exe"); + dnsExeSet.Add(Utils.GetExeName(baseExeName)); } + directExeSet.Add(Utils.GetExeName(baseExeName)); } } + + lstDnsExe = new List(dnsExeSet); + lstDirectExe = new List(directExeSet); } private async Task GenRoutingUserRule(RulesItem item, List rules) @@ -1087,14 +1090,11 @@ public class CoreConfigSingboxService if (item.Port.IsNotEmpty()) { - if (item.Port.Contains("-")) - { - rule.port_range = new List { item.Port.Replace("-", ":") }; - } - else - { - rule.port = new List { item.Port.ToInt() }; - } + var portRanges = item.Port.Split(',').Where(it => it.Contains('-')).Select(it => it.Replace("-", ":")).ToList(); + var ports = item.Port.Split(',').Where(it => !it.Contains('-')).Select(it => it.ToInt()).ToList(); + + rule.port_range = portRanges.Count > 0 ? portRanges : null; + rule.port = ports.Count > 0 ? ports : null; } if (item.Network.IsNotEmpty()) { @@ -1151,7 +1151,7 @@ public class CoreConfigSingboxService } if (!hasDomainIp - && (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null)) + && (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null || rule.network != null)) { rules.Add(rule); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 618f5da8..b1dc1ffd 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -328,7 +328,7 @@ public class CoreConfigV2rayService { listen = Global.Loopback, port = port, - protocol = EInboundProtocol.socks.ToString(), + protocol = EInboundProtocol.mixed.ToString(), }; inbound.tag = inbound.protocol + inbound.port.ToString(); v2rayConfig.inbounds.Add(inbound); @@ -403,7 +403,7 @@ public class CoreConfigV2rayService tag = $"{EInboundProtocol.socks}{port}", listen = Global.Loopback, port = port, - protocol = EInboundProtocol.socks.ToString(), + protocol = EInboundProtocol.mixed.ToString(), }); ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); @@ -507,7 +507,7 @@ public class CoreConfigV2rayService } inbound.tag = protocol.ToString(); inbound.port = inItem.LocalPort + (int)protocol; - inbound.protocol = EInboundProtocol.socks.ToString(); + inbound.protocol = EInboundProtocol.mixed.ToString(); inbound.settings.udp = inItem.UdpEnabled; inbound.sniffing.enabled = inItem.SniffingEnabled; inbound.sniffing.destOverride = inItem.DestOverride; @@ -614,6 +614,7 @@ public class CoreConfigV2rayService if (rule.port.IsNotEmpty() || rule.protocol?.Count > 0 || rule.inboundTag?.Count > 0 + || rule.network != null ) { var it = JsonUtils.DeepCopy(rule); diff --git a/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayN/ServiceLib/Services/UpdateService.cs index f896e4ad..636f3616 100644 --- a/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayN/ServiceLib/Services/UpdateService.cs @@ -194,11 +194,11 @@ public class UpdateService { if (Utils.IsBase64String(result2)) { - result += Utils.Base64Decode(result2); + result += Environment.NewLine + Utils.Base64Decode(result2); } else { - result += result2; + result += Environment.NewLine + result2; } } } @@ -243,21 +243,6 @@ public class UpdateService _updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); } - public async Task RunAvailabilityCheck() - { - var downloadHandle = new DownloadService(); - var time = await downloadHandle.RunAvailabilityCheck(null); - var ip = Global.None; - if (time > 0) - { - var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl); - var ipInfo = JsonUtils.Deserialize(result); - ip = $"({ipInfo?.country_code}) {ipInfo?.ip}"; - } - - return string.Format(ResUI.TestMeOutput, time, ip); - } - #region CheckUpdate private private async Task CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease) diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index fd26eb32..57d8cac7 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -70,6 +70,7 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public string GeoFileSourceUrl { get; set; } [Reactive] public string SrsFileSourceUrl { get; set; } [Reactive] public string RoutingRulesSourceUrl { get; set; } + [Reactive] public string IPAPIUrl { get; set; } #endregion UI @@ -186,6 +187,7 @@ public class OptionSettingViewModel : MyReactiveObject GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl; SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl; RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl; + IPAPIUrl = _config.SpeedTestItem.IPAPIUrl; #endregion UI @@ -344,6 +346,7 @@ public class OptionSettingViewModel : MyReactiveObject _config.ConstItem.GeoSourceUrl = GeoFileSourceUrl; _config.ConstItem.SrsSourceUrl = SrsFileSourceUrl; _config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl; + _config.SpeedTestItem.IPAPIUrl = IPAPIUrl; //systemProxy _config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions; @@ -356,7 +359,7 @@ public class OptionSettingViewModel : MyReactiveObject _config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; - + //coreType await SaveCoreType(); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 2c48d0bf..82b48aff 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -320,7 +320,7 @@ public class StatusBarViewModel : MyReactiveObject var msg = await Task.Run(async () => { - return await (new UpdateService()).RunAvailabilityCheck(); + return await ConnectionHandler.Instance.RunAvailabilityCheck(); }); NoticeHandler.Instance.SendMessageEx(msg); diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index b797c90b..7626edfd 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -43,7 +43,7 @@ public partial class App : Application { if (e.ExceptionObject != null) { - Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); + Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject); } } diff --git a/v2rayN/v2rayN.Desktop/Views/ClashConnectionsView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ClashConnectionsView.axaml.cs index b4426c29..c39724eb 100644 --- a/v2rayN/v2rayN.Desktop/Views/ClashConnectionsView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ClashConnectionsView.axaml.cs @@ -52,9 +52,16 @@ public partial class ClashConnectionsView : ReactiveUserControl private async void MenuClose_Click(object? sender, RoutedEventArgs e) { - if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No) + if (await UI.ShowYesNo(this, ResUI.menuExitTips) != ButtonResult.Yes) { return; } diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 3ad9b178..004f68a4 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -343,7 +343,7 @@ + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index 4faa0c27..fd319d92 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -92,6 +92,10 @@ public partial class OptionSettingWindow : ReactiveWindow + { + cmbIPAPIUrl.Items.Add(it); + }); foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) { cmbMainGirdOrientation.Items.Add(it.ToString()); @@ -143,6 +147,7 @@ public partial class OptionSettingWindow : ReactiveWindow vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs index 6414b40d..9bcdb85a 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs @@ -138,7 +138,7 @@ public partial class ProfilesView : ReactiveUserControl break; case EViewAction.ShowYesNo: - if (await UI.ShowYesNo(_window, ResUI.RemoveServer) == ButtonResult.No) + if (await UI.ShowYesNo(_window, ResUI.RemoveServer) != ButtonResult.Yes) { return false; } @@ -345,9 +345,16 @@ public partial class ProfilesView : ReactiveUserControl private void AutofitColumnWidth() { - foreach (var it in lstProfiles.Columns) + try { - it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + foreach (var it in lstProfiles.Columns) + { + it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + } + } + catch (Exception ex) + { + Logging.SaveLog("ProfilesView", ex); } } diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs index 1fbe5c2b..1cae4c0b 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs @@ -80,14 +80,14 @@ public partial class RoutingRuleSettingWindow : ReactiveWindow break; case EViewAction.ShowYesNo: - if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No) + if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes) { return false; } diff --git a/v2rayN/v2rayN/App.xaml.cs b/v2rayN/v2rayN/App.xaml.cs index 86518fa0..0a282733 100644 --- a/v2rayN/v2rayN/App.xaml.cs +++ b/v2rayN/v2rayN/App.xaml.cs @@ -56,7 +56,7 @@ public partial class App : Application { if (e.ExceptionObject != null) { - Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); + Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject); } } diff --git a/v2rayN/v2rayN/Views/ClashConnectionsView.xaml.cs b/v2rayN/v2rayN/Views/ClashConnectionsView.xaml.cs index 54f3adee..18b97d2a 100644 --- a/v2rayN/v2rayN/Views/ClashConnectionsView.xaml.cs +++ b/v2rayN/v2rayN/Views/ClashConnectionsView.xaml.cs @@ -55,9 +55,16 @@ public partial class ClashConnectionsView private void AutofitColumnWidth() { - foreach (var it in lstConnections.Columns) + try { - it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + foreach (var it in lstConnections.Columns) + { + it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + } + } + catch (Exception ex) + { + Logging.SaveLog("ClashConnectionsView", ex); } } diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 374dce35..f9b4b13c 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -552,6 +552,7 @@ + @@ -942,6 +943,22 @@ Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}" TextWrapping="Wrap" /> + + + diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index b0b5440c..ecdc39f0 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -101,6 +101,10 @@ public partial class OptionSettingWindow { cmbRoutingRulesSourceUrl.Items.Add(it); }); + Global.IPAPIUrls.ForEach(it => + { + cmbIPAPIUrl.Items.Add(it); + }); foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) { cmbMainGirdOrientation.Items.Add(it.ToString()); @@ -162,6 +166,7 @@ public partial class OptionSettingWindow this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml b/v2rayN/v2rayN/Views/ProfilesView.xaml index 871572c1..393bf062 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml @@ -25,6 +25,7 @@ diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index daefd332..3501747e 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -323,9 +323,16 @@ public partial class ProfilesView private void AutofitColumnWidth() { - foreach (var it in lstProfiles.Columns) + try { - it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + foreach (var it in lstProfiles.Columns) + { + it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); + } + } + catch (Exception ex) + { + Logging.SaveLog("ProfilesView", ex); } } diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml b/v2rayN/v2rayN/Views/StatusBarView.xaml index d337c4cd..bc5534db 100644 --- a/v2rayN/v2rayN/Views/StatusBarView.xaml +++ b/v2rayN/v2rayN/Views/StatusBarView.xaml @@ -92,7 +92,8 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}" DisplayMemberPath="Remarks" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFloatingHintComboBox}" /> + Style="{StaticResource MaterialDesignFloatingHintComboBox}"> + @@ -184,7 +185,8 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}" DisplayMemberPath="Remarks" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFilledComboBox}" /> + Style="{StaticResource MaterialDesignFilledComboBox}"> + @@ -198,7 +200,8 @@ AutomationProperties.Name="{x:Static resx:ResUI.menuServers}" DisplayMemberPath="Text" FontSize="{DynamicResource StdFontSize}" - Style="{StaticResource MaterialDesignFilledComboBox}" /> + Style="{StaticResource MaterialDesignFilledComboBox}"> +