Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5efb784115 | ||
|
|
acde5b8384 | ||
|
|
373ee6586a | ||
|
|
215308c329 | ||
|
|
478cadba5e | ||
|
|
f4af4da791 | ||
|
|
22d4f435de | ||
|
|
ccb8cd5c04 | ||
|
|
e963f9e349 | ||
|
|
e3c2a4b8da | ||
|
|
324f46cdad | ||
|
|
691b19b5fd | ||
|
|
63ed2311dc | ||
|
|
d59e59ca40 | ||
|
|
c9a278b4a2 | ||
|
|
8ee5304148 | ||
|
|
3beaa8145f | ||
|
|
e30c55a0af | ||
|
|
b583590a64 | ||
|
|
a28c63168a | ||
|
|
620422350f | ||
|
|
b77cc3c33b | ||
|
|
71bf9b4887 | ||
|
|
9039d401da | ||
|
|
74a0a93201 | ||
|
|
522571f0b3 | ||
|
|
123c49c22d | ||
|
|
1ff88d29be | ||
|
|
63d5a2a1be | ||
|
|
1e9a6cb06b | ||
|
|
31748aa660 | ||
|
|
d0c6ea6a63 | ||
|
|
7ffe286a56 | ||
|
|
b99b30163b | ||
|
|
83b4f1e660 | ||
|
|
b57ba6a98b | ||
|
|
d748e6eff4 | ||
|
|
315d4b75b2 | ||
|
|
31267cbc33 | ||
|
|
c23379b3b6 | ||
|
|
568144d6a2 | ||
|
|
5857042963 | ||
|
|
28e41dc621 | ||
|
|
baef3b364b | ||
|
|
4b9ddb803f | ||
|
|
c07c7ad82f | ||
|
|
e223b80b95 | ||
|
|
5fe468fa1b | ||
|
|
60068d8d16 | ||
|
|
b6c5b46afe | ||
|
|
124cbfadb4 | ||
|
|
8d21f9b900 | ||
|
|
398dbbd2e5 | ||
|
|
e9b392d1c0 | ||
|
|
9d7c7e3225 | ||
|
|
855fd4f0b7 | ||
|
|
807839929d | ||
|
|
870955fee1 | ||
|
|
e0cea929ea | ||
|
|
315f4c35f0 | ||
|
|
d961ea22a6 | ||
|
|
5c0c07c78f | ||
|
|
bba93a0fb7 | ||
|
|
5683df2fc0 | ||
|
|
b5cb9ce67e | ||
|
|
06a32dc895 | ||
|
|
dff12a8a3a | ||
|
|
dc3f07ee84 | ||
|
|
1aef49ee11 | ||
|
|
ee3159b00e | ||
|
|
86d2c307f1 |
@@ -9,9 +9,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.62.0">
|
||||
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.63.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.64.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Application
|
||||
x:Class="v2rayN.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
ShutdownMode="OnExplicitShutdown"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
@@ -13,13 +13,14 @@
|
||||
BaseTheme="Light"
|
||||
PrimaryColor="Blue"
|
||||
SecondaryColor="Lime" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<system:Double x:Key="MenuItemHeight">26</system:Double>
|
||||
<system:Double x:Key="StdFontSize">12</system:Double>
|
||||
<system:Double x:Key="StdFontSize1">13</system:Double>
|
||||
<system:Double x:Key="StdFontSize2">14</system:Double>
|
||||
<system:Double x:Key="StdFontSizeMsg">11</system:Double>
|
||||
<system:Double x:Key="StdFontSize-1">11</system:Double>
|
||||
|
||||
<conv:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||
<Thickness
|
||||
@@ -68,16 +69,21 @@
|
||||
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" />
|
||||
</Style>
|
||||
<Style x:Key="lvItemSelected" TargetType="{x:Type ListViewItem}">
|
||||
<Setter Property="Height" Value="20" />
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsSelected" Value="true">
|
||||
<Setter Property="Background" Value="{DynamicResource PrimaryHueLightBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Margin" Value="2" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<materialDesign:Card Name="_Card" SnapsToDevicePixels="true">
|
||||
<ContentPresenter />
|
||||
</materialDesign:Card>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsSelected" Value="true">
|
||||
<Setter TargetName="_Card" Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GridViewColumnHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
|
||||
@@ -156,6 +162,27 @@
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" />
|
||||
<Setter Property="Padding" Value="{StaticResource OutlinedTextBoxDefaultPadding}" />
|
||||
</Style>
|
||||
<Style x:Key="ListItemChip" TargetType="{x:Type materialDesign:Chip}">
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize1}" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ListItemTitle"
|
||||
BasedOn="{StaticResource MaterialDesignTextBlock}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize1}" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ListItemSubTitle"
|
||||
BasedOn="{StaticResource MaterialDesignTextBlock}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ListItemSubTitle2"
|
||||
BasedOn="{StaticResource MaterialDesignTextBlock}"
|
||||
TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="FontSize" Value="{DynamicResource StdFontSize-1}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -43,6 +43,7 @@ namespace v2rayN
|
||||
Init();
|
||||
Logging.LoggingEnabled(_config.guiItem.enableLog);
|
||||
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
|
||||
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||
Logging.ClearLogs();
|
||||
|
||||
Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage);
|
||||
@@ -58,10 +59,12 @@ namespace v2rayN
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
|
||||
//{
|
||||
// _config.guiItem.enableStatistics = false;
|
||||
//}
|
||||
|
||||
//Under Win10
|
||||
if (Environment.OSVersion.Version.Major < 10)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
|
||||
}
|
||||
}
|
||||
|
||||
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
@@ -21,6 +22,22 @@ namespace v2rayN
|
||||
|
||||
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await httpClient.GetAsync(url);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
@@ -41,6 +58,21 @@ namespace v2rayN
|
||||
var result = await httpClient.PutAsync(url, content);
|
||||
}
|
||||
|
||||
public async Task PatchAsync(string url, Dictionary<string, string> headers)
|
||||
{
|
||||
var myContent = JsonUtils.Serialize(headers);
|
||||
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
|
||||
var byteContent = new ByteArrayContent(buffer);
|
||||
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
|
||||
await httpClient.PatchAsync(url, byteContent);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string url)
|
||||
{
|
||||
await httpClient.DeleteAsync(url);
|
||||
}
|
||||
|
||||
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(url);
|
||||
|
||||
@@ -105,9 +105,12 @@ namespace v2rayN
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="nullValue"></param>
|
||||
/// <returns></returns>
|
||||
public static int ToFile(object? obj, string filePath, bool nullValue = true)
|
||||
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
|
||||
{
|
||||
int result;
|
||||
if (filePath is null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
try
|
||||
{
|
||||
using FileStream file = File.Create(filePath);
|
||||
@@ -119,14 +122,13 @@ namespace v2rayN
|
||||
};
|
||||
|
||||
JsonSerializer.Serialize(file, obj, options);
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
result = -1;
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,13 @@
|
||||
using QRCoder;
|
||||
using QRCoder.Xaml;
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode;
|
||||
using ZXing.Windows.Compatibility;
|
||||
|
||||
namespace v2rayN
|
||||
{
|
||||
@@ -28,5 +35,70 @@ namespace v2rayN
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ScanScreen(float dpiX, float dpiY)
|
||||
{
|
||||
try
|
||||
{
|
||||
var left = (int)(SystemParameters.WorkArea.Left);
|
||||
var top = (int)(SystemParameters.WorkArea.Top);
|
||||
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
|
||||
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
|
||||
|
||||
using Bitmap fullImage = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(fullImage))
|
||||
{
|
||||
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
|
||||
}
|
||||
int maxTry = 10;
|
||||
for (int i = 0; i < maxTry; i++)
|
||||
{
|
||||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
|
||||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
|
||||
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
|
||||
Bitmap target = new(width, height);
|
||||
|
||||
double imageScale = (double)width / (double)cropRect.Width;
|
||||
using (Graphics g = Graphics.FromImage(target))
|
||||
{
|
||||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
|
||||
cropRect,
|
||||
GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
BitmapLuminanceSource source = new(target);
|
||||
QRCodeReader reader = new();
|
||||
|
||||
BinaryBitmap bitmap = new(new HybridBinarizer(source));
|
||||
var result = reader.decode(bitmap);
|
||||
if (result != null)
|
||||
{
|
||||
return result.Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
BinaryBitmap bitmap2 = new(new HybridBinarizer(source.invert()));
|
||||
var result2 = reader.decode(bitmap2);
|
||||
if (result2 != null)
|
||||
{
|
||||
return result2.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Tuple<float, float> GetDpiXY(Window window)
|
||||
{
|
||||
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
|
||||
Graphics g = Graphics.FromHwnd(hWnd);
|
||||
|
||||
return new(96 / g.DpiX, 96 / g.DpiY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,6 @@ using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode;
|
||||
using ZXing.Windows.Compatibility;
|
||||
|
||||
namespace v2rayN
|
||||
{
|
||||
@@ -57,11 +53,14 @@ namespace v2rayN
|
||||
/// 取得存储资源
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? LoadResource(string res)
|
||||
public static string? LoadResource(string? res)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(res)) return null;
|
||||
if (!File.Exists(res))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return File.ReadAllText(res);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -80,7 +79,7 @@ namespace v2rayN
|
||||
/// </summary>
|
||||
/// <param name="lst"></param>
|
||||
/// <returns></returns>
|
||||
public static string List2String(List<string> lst, bool wrap = false)
|
||||
public static string List2String(List<string>? lst, bool wrap = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -478,7 +477,7 @@ namespace v2rayN
|
||||
/// 验证Domain地址是否合法
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
public static bool IsDomain(string domain)
|
||||
public static bool IsDomain(string? domain)
|
||||
{
|
||||
//如果为空
|
||||
if (IsNullOrEmpty(domain))
|
||||
@@ -741,11 +740,11 @@ namespace v2rayN
|
||||
return Guid.TryParse(strSrc, out Guid g);
|
||||
}
|
||||
|
||||
public static void ProcessStart(string fileName)
|
||||
public static void ProcessStart(string fileName, string arguments = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -947,66 +946,6 @@ namespace v2rayN
|
||||
|
||||
#endregion TempPath
|
||||
|
||||
#region scan screen
|
||||
|
||||
public static string ScanScreen(float dpiX, float dpiY)
|
||||
{
|
||||
try
|
||||
{
|
||||
var left = (int)(SystemParameters.WorkArea.Left);
|
||||
var top = (int)(SystemParameters.WorkArea.Top);
|
||||
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
|
||||
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
|
||||
|
||||
using Bitmap fullImage = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(fullImage))
|
||||
{
|
||||
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
|
||||
}
|
||||
int maxTry = 10;
|
||||
for (int i = 0; i < maxTry; i++)
|
||||
{
|
||||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
|
||||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
|
||||
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
|
||||
Bitmap target = new(width, height);
|
||||
|
||||
double imageScale = (double)width / (double)cropRect.Width;
|
||||
using (Graphics g = Graphics.FromImage(target))
|
||||
{
|
||||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
|
||||
cropRect,
|
||||
GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
BitmapLuminanceSource source = new(target);
|
||||
BinaryBitmap bitmap = new(new HybridBinarizer(source));
|
||||
QRCodeReader reader = new();
|
||||
Result result = reader.decode(bitmap);
|
||||
if (result != null)
|
||||
{
|
||||
string ret = result.Text;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Tuple<float, float> GetDpiXY(Window window)
|
||||
{
|
||||
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
|
||||
Graphics g = Graphics.FromHwnd(hWnd);
|
||||
|
||||
return new(96 / g.DpiX, 96 / g.DpiY);
|
||||
}
|
||||
|
||||
#endregion scan screen
|
||||
|
||||
#region 开机自动启动等
|
||||
|
||||
/// <summary>
|
||||
@@ -1145,8 +1084,10 @@ namespace v2rayN
|
||||
{
|
||||
try
|
||||
{
|
||||
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
|
||||
var guid = new Guid(sum);
|
||||
string pnputilPath = @"C:\Windows\System32\pnputil.exe";
|
||||
string arg = $" /remove-device /deviceid \"wintun\"";
|
||||
string arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
|
||||
|
||||
// Try to remove the device
|
||||
Process proc = new()
|
||||
@@ -1186,4 +1127,4 @@ namespace v2rayN
|
||||
|
||||
#endregion Windows API
|
||||
}
|
||||
}
|
||||
}
|
||||
63
v2rayN/v2rayN/Common/YamlUtils.cs
Normal file
63
v2rayN/v2rayN/Common/YamlUtils.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace v2rayN.Common
|
||||
{
|
||||
internal class YamlUtils
|
||||
{
|
||||
#region YAML
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化成对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static T FromYaml<T>(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
T obj = deserializer.Deserialize<T>(str);
|
||||
return obj;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("FromYaml", ex);
|
||||
return deserializer.Deserialize<T>("");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(Object obj)
|
||||
{
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
string result = string.Empty;
|
||||
try
|
||||
{
|
||||
result = serializer.Serialize(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion YAML
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EConfigType
|
||||
{
|
||||
@@ -10,6 +10,7 @@
|
||||
Trojan = 6,
|
||||
Hysteria2 = 7,
|
||||
Tuic = 8,
|
||||
Wireguard = 9
|
||||
Wireguard = 9,
|
||||
Http = 10
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum ECoreType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EGlobalHotkey
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EInboundProtocol
|
||||
{
|
||||
@@ -8,6 +8,7 @@
|
||||
http2,
|
||||
pac,
|
||||
api,
|
||||
api2,
|
||||
speedtest = 21
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EMove
|
||||
{
|
||||
10
v2rayN/v2rayN/Enums/ERuleMode.cs
Normal file
10
v2rayN/v2rayN/Enums/ERuleMode.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum ERuleMode
|
||||
{
|
||||
Rule = 0,
|
||||
Global = 1,
|
||||
Direct = 2,
|
||||
Unchanged = 3
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EServerColName
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum ESpeedActionType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum ESysProxyType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum ETransport
|
||||
{
|
||||
@@ -6,6 +6,7 @@
|
||||
kcp,
|
||||
ws,
|
||||
httpupgrade,
|
||||
splithttp,
|
||||
h2,
|
||||
http,
|
||||
quic,
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace v2rayN.Enums
|
||||
{
|
||||
public enum EViewAction
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN
|
||||
{
|
||||
@@ -23,12 +23,14 @@ namespace v2rayN
|
||||
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
|
||||
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
|
||||
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
|
||||
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
|
||||
|
||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||
public const string ConfigFileName = "guiNConfig.json";
|
||||
public const string CoreConfigFileName = "config.json";
|
||||
public const string CorePreConfigFileName = "configPre.json";
|
||||
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
|
||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||
public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
|
||||
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
|
||||
public const string V2raySampleHttpRequestFileName = "v2rayN.Sample.SampleHttpRequest";
|
||||
@@ -42,6 +44,8 @@ namespace v2rayN
|
||||
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
|
||||
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal";
|
||||
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
|
||||
public const string ClashMixinYaml = "v2rayN.Sample.clash_mixin_yaml";
|
||||
public const string ClashTunYaml = "v2rayN.Sample.clash_tun_yaml";
|
||||
|
||||
public const string DefaultSecurity = "auto";
|
||||
public const string DefaultNetwork = "tcp";
|
||||
@@ -105,7 +109,6 @@ namespace v2rayN
|
||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||
@"http://cachefly.cachefly.net/50mb.test",
|
||||
@"http://cachefly.cachefly.net/100mb.test",
|
||||
@"http://cachefly.cachefly.net/10mb.test"
|
||||
};
|
||||
|
||||
@@ -143,6 +146,7 @@ namespace v2rayN
|
||||
{EConfigType.VMess,"vmess"},
|
||||
{EConfigType.Shadowsocks,"shadowsocks"},
|
||||
{EConfigType.Socks,"socks"},
|
||||
{EConfigType.Http,"http"},
|
||||
{EConfigType.VLESS,"vless"},
|
||||
{EConfigType.Trojan,"trojan"},
|
||||
{EConfigType.Hysteria2,"hysteria2"},
|
||||
@@ -156,7 +160,7 @@ namespace v2rayN
|
||||
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
|
||||
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
|
||||
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "splithttp", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||
public static readonly List<string> CoreTypes = new() { "v2fly", "SagerNet", "Xray", "sing_box" };
|
||||
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
|
||||
@@ -173,12 +177,18 @@ namespace v2rayN
|
||||
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
||||
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
||||
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
||||
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
||||
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
||||
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
|
||||
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
|
||||
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
|
||||
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
|
||||
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
|
||||
|
||||
public static readonly List<string> allowSelectType = new List<string> { "selector", "urltest", "loadbalance", "fallback" };
|
||||
public static readonly List<string> notAllowTestType = new List<string> { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
|
||||
public static readonly List<string> proxyVehicleType = new List<string> { "file", "http" };
|
||||
|
||||
#endregion const
|
||||
}
|
||||
}
|
||||
218
v2rayN/v2rayN/Handler/ClashApiHandler.cs
Normal file
218
v2rayN/v2rayN/Handler/ClashApiHandler.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using v2rayN.Models;
|
||||
using static v2rayN.Models.ClashProxies;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
{
|
||||
public sealed class ClashApiHandler
|
||||
{
|
||||
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
|
||||
public static ClashApiHandler Instance => instance.Value;
|
||||
|
||||
private Dictionary<String, ProxiesItem> _proxies;
|
||||
public Dictionary<string, object> ProfileContent { get; set; }
|
||||
|
||||
public void SetProxies(Dictionary<String, ProxiesItem> proxies)
|
||||
{
|
||||
_proxies = proxies;
|
||||
}
|
||||
|
||||
public Dictionary<String, ProxiesItem> GetProxies()
|
||||
{
|
||||
return _proxies;
|
||||
}
|
||||
|
||||
public void GetClashProxies(Config config, Action<ClashProxies, ClashProviders> update)
|
||||
{
|
||||
Task.Run(() => GetClashProxiesAsync(config, update));
|
||||
}
|
||||
|
||||
private async Task GetClashProxiesAsync(Config config, Action<ClashProxies, ClashProviders> update)
|
||||
{
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
|
||||
|
||||
var url2 = $"{GetApiUrl()}/providers/proxies";
|
||||
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
|
||||
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
|
||||
|
||||
if (clashProxies != null || clashProviders != null)
|
||||
{
|
||||
update(clashProxies, clashProviders);
|
||||
return;
|
||||
}
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
update(null, null);
|
||||
}
|
||||
|
||||
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> update)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (blAll)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (GetProxies() != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
var proxies = GetProxies();
|
||||
if (proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lstProxy = new List<ClashProxyModel>();
|
||||
foreach (KeyValuePair<string, ProxiesItem> kv in proxies)
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
lstProxy.Add(new ClashProxyModel()
|
||||
{
|
||||
name = kv.Value.name,
|
||||
type = kv.Value.type.ToLower(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (lstProxy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var urlBase = $"{GetApiUrl()}/proxies";
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + LazyConfig.Instance.GetConfig().speedTestItem.speedPingTestUrl;
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var it in lstProxy)
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(it.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var name = it.name;
|
||||
var url = string.Format(urlBase, name);
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
update(it, result);
|
||||
}));
|
||||
}
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
Thread.Sleep(1000);
|
||||
update(null, "");
|
||||
});
|
||||
}
|
||||
|
||||
public List<ProxiesItem>? GetClashProxyGroups()
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = ProfileContent;
|
||||
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GetClashProxyGroups", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async void ClashSetActiveProxy(string name, string nameNode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies/{name}";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("name", nameNode);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClashConfigUpdate(Dictionary<string, string> headers)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var proxies = GetProxies();
|
||||
if (proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var urlBase = $"{GetApiUrl()}/configs";
|
||||
|
||||
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
|
||||
});
|
||||
}
|
||||
|
||||
public async void ClashConfigReload(string filePath)
|
||||
{
|
||||
ClashConnectionClose("");
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/configs?force=true";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("path", filePath);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetClashConnections(Config config, Action<ClashConnections> update)
|
||||
{
|
||||
Task.Run(() => GetClashConnectionsAsync(config, update));
|
||||
}
|
||||
|
||||
private async Task GetClashConnectionsAsync(Config config, Action<ClashConnections> update)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
|
||||
|
||||
update(clashConnections);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async void ClashConnectionClose(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections/{id}";
|
||||
await HttpClientHelper.Instance.DeleteAsync(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetApiUrl()
|
||||
{
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{LazyConfig.Instance.StatePort2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler.Fmt;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
@@ -50,7 +53,6 @@ namespace v2rayN.Handler
|
||||
{
|
||||
logEnabled = false,
|
||||
loglevel = "warning",
|
||||
|
||||
muxEnabled = false,
|
||||
};
|
||||
}
|
||||
@@ -180,7 +182,7 @@ namespace v2rayN.Handler
|
||||
config.mux4SboxItem = new()
|
||||
{
|
||||
protocol = Global.SingboxMuxs[0],
|
||||
max_connections = 4
|
||||
max_connections = 8
|
||||
};
|
||||
}
|
||||
|
||||
@@ -192,6 +194,7 @@ namespace v2rayN.Handler
|
||||
down_mbps = 100
|
||||
};
|
||||
}
|
||||
config.clashUIItem ??= new();
|
||||
|
||||
LazyConfig.Instance.SetConfig(config);
|
||||
return 0;
|
||||
@@ -241,103 +244,103 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
public static int ImportOldGuiConfig(Config config, string fileName)
|
||||
{
|
||||
var result = Utils.LoadResource(fileName);
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
//public static int ImportOldGuiConfig(Config config, string fileName)
|
||||
//{
|
||||
// var result = Utils.LoadResource(fileName);
|
||||
// if (Utils.IsNullOrEmpty(result))
|
||||
// {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
var configOld = JsonUtils.Deserialize<ConfigOld>(result);
|
||||
if (configOld == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// var configOld = JsonUtils.Deserialize<ConfigOld>(result);
|
||||
// if (configOld == null)
|
||||
// {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
var subItem = JsonUtils.Deserialize<List<SubItem>>(JsonUtils.Serialize(configOld.subItem));
|
||||
foreach (var it in subItem)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(it.id))
|
||||
{
|
||||
it.id = Utils.GetGUID(false);
|
||||
}
|
||||
SQLiteHelper.Instance.Replace(it);
|
||||
}
|
||||
// var subItem = JsonUtils.Deserialize<List<SubItem>>(JsonUtils.Serialize(configOld.subItem));
|
||||
// foreach (var it in subItem)
|
||||
// {
|
||||
// if (Utils.IsNullOrEmpty(it.id))
|
||||
// {
|
||||
// it.id = Utils.GetGUID(false);
|
||||
// }
|
||||
// SQLiteHelper.Instance.Replace(it);
|
||||
// }
|
||||
|
||||
var profileItems = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(configOld.vmess));
|
||||
foreach (var it in profileItems)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(it.indexId))
|
||||
{
|
||||
it.indexId = Utils.GetGUID(false);
|
||||
}
|
||||
SQLiteHelper.Instance.Replace(it);
|
||||
}
|
||||
// var profileItems = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(configOld.vmess));
|
||||
// foreach (var it in profileItems)
|
||||
// {
|
||||
// if (Utils.IsNullOrEmpty(it.indexId))
|
||||
// {
|
||||
// it.indexId = Utils.GetGUID(false);
|
||||
// }
|
||||
// SQLiteHelper.Instance.Replace(it);
|
||||
// }
|
||||
|
||||
foreach (var it in configOld.routings)
|
||||
{
|
||||
if (it.locked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var routing = JsonUtils.Deserialize<RoutingItem>(JsonUtils.Serialize(it));
|
||||
foreach (var it2 in it.rules)
|
||||
{
|
||||
it2.id = Utils.GetGUID(false);
|
||||
}
|
||||
routing.ruleNum = it.rules.Count;
|
||||
routing.ruleSet = JsonUtils.Serialize(it.rules, false);
|
||||
// foreach (var it in configOld.routings)
|
||||
// {
|
||||
// if (it.locked)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// var routing = JsonUtils.Deserialize<RoutingItem>(JsonUtils.Serialize(it));
|
||||
// foreach (var it2 in it.rules)
|
||||
// {
|
||||
// it2.id = Utils.GetGUID(false);
|
||||
// }
|
||||
// routing.ruleNum = it.rules.Count;
|
||||
// routing.ruleSet = JsonUtils.Serialize(it.rules, false);
|
||||
|
||||
if (Utils.IsNullOrEmpty(routing.id))
|
||||
{
|
||||
routing.id = Utils.GetGUID(false);
|
||||
}
|
||||
SQLiteHelper.Instance.Replace(routing);
|
||||
}
|
||||
// if (Utils.IsNullOrEmpty(routing.id))
|
||||
// {
|
||||
// routing.id = Utils.GetGUID(false);
|
||||
// }
|
||||
// SQLiteHelper.Instance.Replace(routing);
|
||||
// }
|
||||
|
||||
config = JsonUtils.Deserialize<Config>(JsonUtils.Serialize(configOld));
|
||||
// config = JsonUtils.Deserialize<Config>(JsonUtils.Serialize(configOld));
|
||||
|
||||
if (config.coreBasicItem == null)
|
||||
{
|
||||
config.coreBasicItem = new()
|
||||
{
|
||||
logEnabled = configOld.logEnabled,
|
||||
loglevel = configOld.loglevel,
|
||||
muxEnabled = configOld.muxEnabled,
|
||||
};
|
||||
}
|
||||
// if (config.coreBasicItem == null)
|
||||
// {
|
||||
// config.coreBasicItem = new()
|
||||
// {
|
||||
// logEnabled = configOld.logEnabled,
|
||||
// loglevel = configOld.loglevel,
|
||||
// muxEnabled = configOld.muxEnabled,
|
||||
// };
|
||||
// }
|
||||
|
||||
if (config.routingBasicItem == null)
|
||||
{
|
||||
config.routingBasicItem = new()
|
||||
{
|
||||
enableRoutingAdvanced = configOld.enableRoutingAdvanced,
|
||||
domainStrategy = configOld.domainStrategy
|
||||
};
|
||||
}
|
||||
// if (config.routingBasicItem == null)
|
||||
// {
|
||||
// config.routingBasicItem = new()
|
||||
// {
|
||||
// enableRoutingAdvanced = configOld.enableRoutingAdvanced,
|
||||
// domainStrategy = configOld.domainStrategy
|
||||
// };
|
||||
// }
|
||||
|
||||
if (config.guiItem == null)
|
||||
{
|
||||
config.guiItem = new()
|
||||
{
|
||||
enableStatistics = configOld.enableStatistics,
|
||||
keepOlderDedupl = configOld.keepOlderDedupl,
|
||||
ignoreGeoUpdateCore = configOld.ignoreGeoUpdateCore,
|
||||
autoUpdateInterval = configOld.autoUpdateInterval,
|
||||
checkPreReleaseUpdate = configOld.checkPreReleaseUpdate,
|
||||
enableSecurityProtocolTls13 = configOld.enableSecurityProtocolTls13,
|
||||
trayMenuServersLimit = configOld.trayMenuServersLimit,
|
||||
};
|
||||
}
|
||||
// if (config.guiItem == null)
|
||||
// {
|
||||
// config.guiItem = new()
|
||||
// {
|
||||
// enableStatistics = configOld.enableStatistics,
|
||||
// keepOlderDedupl = configOld.keepOlderDedupl,
|
||||
// ignoreGeoUpdateCore = configOld.ignoreGeoUpdateCore,
|
||||
// autoUpdateInterval = configOld.autoUpdateInterval,
|
||||
// checkPreReleaseUpdate = configOld.checkPreReleaseUpdate,
|
||||
// enableSecurityProtocolTls13 = configOld.enableSecurityProtocolTls13,
|
||||
// trayMenuServersLimit = configOld.trayMenuServersLimit,
|
||||
// };
|
||||
// }
|
||||
|
||||
GetDefaultServer(config);
|
||||
GetDefaultRouting(config);
|
||||
SaveConfig(config);
|
||||
LoadConfig(ref config);
|
||||
// GetDefaultServer(config);
|
||||
// GetDefaultRouting(config);
|
||||
// SaveConfig(config);
|
||||
// LoadConfig(ref config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
#endregion ConfigHandler
|
||||
|
||||
@@ -664,6 +667,23 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit server
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="profileItem"></param>
|
||||
/// <returns></returns>
|
||||
public static int AddHttpServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.configType = EConfigType.Http;
|
||||
|
||||
profileItem.address = profileItem.address.TrimEx();
|
||||
|
||||
AddServerCommon(config, profileItem, toFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit server
|
||||
/// </summary>
|
||||
@@ -1053,12 +1073,12 @@ namespace v2rayN.Handler
|
||||
/// 批量添加服务器
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="clipboardData"></param>
|
||||
/// <param name="strData"></param>
|
||||
/// <param name="subid"></param>
|
||||
/// <returns>成功导入的数量</returns>
|
||||
private static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
private static int AddBatchServers(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(clipboardData))
|
||||
if (Utils.IsNullOrEmpty(strData))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -1075,7 +1095,7 @@ namespace v2rayN.Handler
|
||||
//Check for duplicate indexId
|
||||
List<string>? lstDbIndexId = null;
|
||||
List<ProfileItem> lstAdd = new();
|
||||
var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
|
||||
var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
|
||||
if (isSub)
|
||||
{
|
||||
arrData = arrData.Distinct();
|
||||
@@ -1091,7 +1111,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
continue;
|
||||
}
|
||||
var profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg);
|
||||
var profileItem = FmtHandler.ResolveConfig(str, out string msg);
|
||||
if (profileItem is null)
|
||||
{
|
||||
continue;
|
||||
@@ -1160,55 +1180,39 @@ namespace v2rayN.Handler
|
||||
return countServers;
|
||||
}
|
||||
|
||||
private static int AddBatchServers4Custom(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
private static int AddBatchServers4Custom(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(clipboardData))
|
||||
if (Utils.IsNullOrEmpty(strData))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var subRemarks = LazyConfig.Instance.GetSubItem(subid)?.remarks;
|
||||
|
||||
//判断str是否包含s的任意一个字符串
|
||||
static bool Contains(string str, params string[] s)
|
||||
List<ProfileItem>? lstProfiles = null;
|
||||
//Is sing-box array configuration
|
||||
if (lstProfiles is null || lstProfiles.Count <= 0)
|
||||
{
|
||||
foreach (var item in s)
|
||||
{
|
||||
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
lstProfiles = SingboxFmt.ResolveFullArray(strData, subRemarks);
|
||||
}
|
||||
|
||||
//Is v2ray array configuration
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(clipboardData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
if (lstProfiles is null || lstProfiles.Count <= 0)
|
||||
{
|
||||
lstProfiles = V2rayFmt.ResolveFullArray(strData, subRemarks);
|
||||
}
|
||||
if (lstProfiles != null && lstProfiles.Count > 0)
|
||||
{
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
foreach (var configObject in configObjects)
|
||||
foreach (var it in lstProfiles)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
|
||||
if (v2rayCon?.inbounds?.Count > 0 && v2rayCon.outbounds?.Count > 0)
|
||||
it.subid = subid;
|
||||
it.isSub = isSub;
|
||||
if (AddCustomServer(config, it, true) == 0)
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
|
||||
File.WriteAllText(fileName, objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.Xray,
|
||||
address = fileName,
|
||||
remarks = v2rayCon.remarks ?? "v2ray_custom",
|
||||
subid = subid,
|
||||
isSub = isSub
|
||||
};
|
||||
|
||||
if (AddCustomServer(config, profileIt, true) == 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0)
|
||||
@@ -1217,58 +1221,39 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
ProfileItem profileItem = new();
|
||||
//Is v2ray configuration
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(clipboardData);
|
||||
if (v2rayConfig?.inbounds?.Count > 0
|
||||
&& v2rayConfig.outbounds?.Count > 0)
|
||||
ProfileItem? profileItem = null;
|
||||
//Is sing-box configuration
|
||||
if (profileItem is null)
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
|
||||
File.WriteAllText(fileName, clipboardData);
|
||||
|
||||
profileItem.coreType = ECoreType.Xray;
|
||||
profileItem.address = fileName;
|
||||
profileItem.remarks = v2rayConfig.remarks ?? "v2ray_custom";
|
||||
profileItem = SingboxFmt.ResolveFull(strData, subRemarks);
|
||||
}
|
||||
//Is v2ray configuration
|
||||
if (profileItem is null)
|
||||
{
|
||||
profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
|
||||
}
|
||||
//Is Clash configuration
|
||||
else if (Contains(clipboardData, "port", "socks-port", "proxies"))
|
||||
if (profileItem is null)
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml");
|
||||
File.WriteAllText(fileName, clipboardData);
|
||||
|
||||
profileItem.coreType = ECoreType.mihomo;
|
||||
profileItem.address = fileName;
|
||||
profileItem.remarks = "clash_custom";
|
||||
profileItem = ClashFmt.ResolveFull(strData, subRemarks);
|
||||
}
|
||||
//Is hysteria configuration
|
||||
else if (Contains(clipboardData, "server", "up", "down", "listen", "<html>", "<body>"))
|
||||
if (profileItem is null)
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
|
||||
File.WriteAllText(fileName, clipboardData);
|
||||
|
||||
profileItem.coreType = ECoreType.hysteria;
|
||||
profileItem.address = fileName;
|
||||
profileItem.remarks = "hysteria_custom";
|
||||
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
|
||||
}
|
||||
if (profileItem is null)
|
||||
{
|
||||
profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
|
||||
}
|
||||
//Is naiveproxy configuration
|
||||
else if (Contains(clipboardData, "listen", "proxy", "<html>", "<body>"))
|
||||
if (profileItem is null)
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
|
||||
File.WriteAllText(fileName, clipboardData);
|
||||
|
||||
profileItem.coreType = ECoreType.naiveproxy;
|
||||
profileItem.address = fileName;
|
||||
profileItem.remarks = "naiveproxy_custom";
|
||||
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
|
||||
}
|
||||
//Is Other configuration
|
||||
else
|
||||
if (profileItem is null || Utils.IsNullOrEmpty(profileItem.address))
|
||||
{
|
||||
return -1;
|
||||
//var fileName = Utile.GetTempPath($"{Utile.GetGUID(false)}.txt");
|
||||
//File.WriteAllText(fileName, clipboardData);
|
||||
|
||||
//profileItem.address = fileName;
|
||||
//profileItem.remarks = "other_custom";
|
||||
}
|
||||
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
@@ -1281,12 +1266,6 @@ namespace v2rayN.Handler
|
||||
}
|
||||
profileItem.subid = subid;
|
||||
profileItem.isSub = isSub;
|
||||
|
||||
if (Utils.IsNullOrEmpty(profileItem.address))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AddCustomServer(config, profileItem, true) == 0)
|
||||
{
|
||||
return 1;
|
||||
@@ -1297,9 +1276,9 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private static int AddBatchServers4SsSIP008(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
private static int AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(clipboardData))
|
||||
if (Utils.IsNullOrEmpty(strData))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -1309,31 +1288,12 @@ namespace v2rayN.Handler
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
|
||||
//SsSIP008
|
||||
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(clipboardData);
|
||||
if (lstSsServer?.Count <= 0)
|
||||
{
|
||||
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(clipboardData);
|
||||
if (ssSIP008?.servers?.Count > 0)
|
||||
{
|
||||
lstSsServer = ssSIP008.servers;
|
||||
}
|
||||
}
|
||||
|
||||
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
|
||||
if (lstSsServer?.Count > 0)
|
||||
{
|
||||
int counter = 0;
|
||||
foreach (var it in lstSsServer)
|
||||
foreach (var ssItem in lstSsServer)
|
||||
{
|
||||
var ssItem = new ProfileItem()
|
||||
{
|
||||
subid = subid,
|
||||
remarks = it.remarks,
|
||||
security = it.method,
|
||||
id = it.password,
|
||||
address = it.server,
|
||||
port = Utils.ToInt(it.server_port)
|
||||
};
|
||||
ssItem.subid = subid;
|
||||
ssItem.isSub = isSub;
|
||||
if (AddShadowsocksServer(config, ssItem) == 0)
|
||||
@@ -1348,7 +1308,7 @@ namespace v2rayN.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub)
|
||||
public static int AddBatchServers(Config config, string strData, string subid, bool isSub)
|
||||
{
|
||||
List<ProfileItem>? lstOriSub = null;
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
@@ -1357,28 +1317,28 @@ namespace v2rayN.Handler
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
if (Utils.IsBase64String(clipboardData))
|
||||
if (Utils.IsBase64String(strData))
|
||||
{
|
||||
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub);
|
||||
counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
|
||||
}
|
||||
if (counter < 1)
|
||||
{
|
||||
counter = AddBatchServers(config, clipboardData, subid, isSub, lstOriSub);
|
||||
counter = AddBatchServers(config, strData, subid, isSub, lstOriSub);
|
||||
}
|
||||
if (counter < 1)
|
||||
{
|
||||
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub);
|
||||
counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
|
||||
}
|
||||
|
||||
if (counter < 1)
|
||||
{
|
||||
counter = AddBatchServers4SsSIP008(config, clipboardData, subid, isSub, lstOriSub);
|
||||
counter = AddBatchServers4SsSIP008(config, strData, subid, isSub, lstOriSub);
|
||||
}
|
||||
|
||||
//maybe other sub
|
||||
if (counter < 1)
|
||||
{
|
||||
counter = AddBatchServers4Custom(config, clipboardData, subid, isSub, lstOriSub);
|
||||
counter = AddBatchServers4Custom(config, strData, subid, isSub, lstOriSub);
|
||||
}
|
||||
|
||||
return counter;
|
||||
@@ -1397,18 +1357,27 @@ namespace v2rayN.Handler
|
||||
public static int AddSubItem(Config config, string url)
|
||||
{
|
||||
//already exists
|
||||
if (SQLiteHelper.Instance.Table<SubItem>().Where(e => e.url == url).Count() > 0)
|
||||
if (SQLiteHelper.Instance.Table<SubItem>().Any(e => e.url == url))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SubItem subItem = new()
|
||||
{
|
||||
id = string.Empty,
|
||||
remarks = "import_sub",
|
||||
url = url
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
var queryVars = HttpUtility.ParseQueryString(uri.Query);
|
||||
subItem.remarks = queryVars["remarks"] ?? "import_sub";
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return AddSubItem(config, subItem);
|
||||
}
|
||||
|
||||
@@ -1516,16 +1485,16 @@ namespace v2rayN.Handler
|
||||
/// AddBatchRoutingRules
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="clipboardData"></param>
|
||||
/// <param name="strData"></param>
|
||||
/// <returns></returns>
|
||||
public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData)
|
||||
public static int AddBatchRoutingRules(ref RoutingItem routingItem, string strData)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(clipboardData))
|
||||
if (Utils.IsNullOrEmpty(strData))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData);
|
||||
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(strData);
|
||||
if (lstRules == null)
|
||||
{
|
||||
return -1;
|
||||
@@ -1657,14 +1626,15 @@ namespace v2rayN.Handler
|
||||
|
||||
public static int InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
|
||||
{
|
||||
var ver = "V3-";
|
||||
var items = LazyConfig.Instance.RoutingItems();
|
||||
if (blImportAdvancedRules || items.Count <= 0)
|
||||
if (blImportAdvancedRules || items.Where(t => t.remarks.StartsWith(ver)).ToList().Count <= 0)
|
||||
{
|
||||
var maxSort = items.Count;
|
||||
//Bypass the mainland
|
||||
var item2 = new RoutingItem()
|
||||
{
|
||||
remarks = "绕过大陆(Whitelist)",
|
||||
remarks = $"{ver}绕过大陆(Whitelist)",
|
||||
url = string.Empty,
|
||||
sort = maxSort + 1,
|
||||
};
|
||||
@@ -1673,7 +1643,7 @@ namespace v2rayN.Handler
|
||||
//Blacklist
|
||||
var item3 = new RoutingItem()
|
||||
{
|
||||
remarks = "黑名单(Blacklist)",
|
||||
remarks = $"{ver}黑名单(Blacklist)",
|
||||
url = string.Empty,
|
||||
sort = maxSort + 2,
|
||||
};
|
||||
@@ -1682,7 +1652,7 @@ namespace v2rayN.Handler
|
||||
//Global
|
||||
var item1 = new RoutingItem()
|
||||
{
|
||||
remarks = "全局(Global)",
|
||||
remarks = $"{ver}全局(Global)",
|
||||
url = string.Empty,
|
||||
sort = maxSort + 3,
|
||||
};
|
||||
|
||||
271
v2rayN/v2rayN/Handler/CoreConfig/CoreConfigClash.cs
Normal file
271
v2rayN/v2rayN/Handler/CoreConfig/CoreConfigClash.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System.IO;
|
||||
using v2rayN.Common;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
internal class CoreConfigClash
|
||||
{
|
||||
private Config _config;
|
||||
|
||||
public CoreConfigClash(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成配置文件
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
public int GenerateClientConfig(ProfileItem node, string? fileName, out string msg)
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = ResUI.InitialConfiguration;
|
||||
|
||||
try
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration + "1";
|
||||
return -1;
|
||||
}
|
||||
|
||||
string tagYamlStr1 = "!<str>";
|
||||
string tagYamlStr2 = "__strn__";
|
||||
string tagYamlStr3 = "!!str";
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
|
||||
|
||||
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (fileContent == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//port
|
||||
fileContent["port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
//socks-port
|
||||
fileContent["socks-port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//log-level
|
||||
fileContent["log-level"] = GetLogLevel(_config.coreBasicItem.loglevel);
|
||||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}";
|
||||
//allow-lan
|
||||
if (_config.inbound[0].allowLANConn)
|
||||
{
|
||||
fileContent["allow-lan"] = "true";
|
||||
fileContent["bind-address"] = "*";
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent["allow-lan"] = "false";
|
||||
}
|
||||
|
||||
//ipv6
|
||||
fileContent["ipv6"] = _config.clashUIItem.enableIPv6;
|
||||
|
||||
//mode
|
||||
if (!fileContent.ContainsKey("mode"))
|
||||
{
|
||||
fileContent["mode"] = ERuleMode.Rule.ToString().ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_config.clashUIItem.ruleMode != ERuleMode.Unchanged)
|
||||
{
|
||||
fileContent["mode"] = _config.clashUIItem.ruleMode.ToString().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
//enable tun mode
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (!string.IsNullOrEmpty(tun))
|
||||
{
|
||||
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
|
||||
if (tunContent != null)
|
||||
fileContent["tun"] = tunContent["tun"];
|
||||
}
|
||||
}
|
||||
|
||||
//Mixin
|
||||
try
|
||||
{
|
||||
MixinContent(fileContent, node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GenerateClientConfigClash-Mixin", ex);
|
||||
}
|
||||
|
||||
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
|
||||
File.WriteAllText(fileName, txtFileNew);
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration + "2";
|
||||
return -1;
|
||||
}
|
||||
|
||||
ClashApiHandler.Instance.ProfileContent = fileContent;
|
||||
|
||||
msg = string.Format(ResUI.SuccessfulConfiguration, $"{node.GetSummary()}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GenerateClientConfigClash", ex);
|
||||
msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
|
||||
{
|
||||
//if (!_config.clashUIItem.enableMixinContent)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var txtFile = File.ReadAllText(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
|
||||
|
||||
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (mixinContent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var item in mixinContent)
|
||||
{
|
||||
if (!_config.tunModeItem.enableTun && item.Key == "tun")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.Key.StartsWith("prepend-")
|
||||
|| item.Key.StartsWith("append-")
|
||||
|| item.Key.StartsWith("removed-"))
|
||||
{
|
||||
ModifyContentMerge(fileContent, item.Key, item.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent[item.Key] = item.Value;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void ModifyContentMerge(Dictionary<string, object> fileContent, string key, object value)
|
||||
{
|
||||
bool blPrepend = false;
|
||||
bool blRemoved = false;
|
||||
if (key.StartsWith("prepend-"))
|
||||
{
|
||||
blPrepend = true;
|
||||
key = key.Replace("prepend-", "");
|
||||
}
|
||||
else if (key.StartsWith("append-"))
|
||||
{
|
||||
blPrepend = false;
|
||||
key = key.Replace("append-", "");
|
||||
}
|
||||
else if (key.StartsWith("removed-"))
|
||||
{
|
||||
blRemoved = true;
|
||||
key = key.Replace("removed-", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blRemoved && !fileContent.ContainsKey(key))
|
||||
{
|
||||
fileContent.Add(key, value);
|
||||
return;
|
||||
}
|
||||
var lstOri = (List<object>)fileContent[key];
|
||||
var lstValue = (List<object>)value;
|
||||
|
||||
if (blRemoved)
|
||||
{
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.RemoveAll(t => t.ToString().StartsWith(item.ToString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (blPrepend)
|
||||
{
|
||||
lstValue.Reverse();
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.Insert(0, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLogLevel(string level)
|
||||
{
|
||||
if (level == "none")
|
||||
{
|
||||
return "silent";
|
||||
}
|
||||
else
|
||||
{
|
||||
return level;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.IO;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace v2rayN.Handler.CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
@@ -24,9 +25,17 @@ namespace v2rayN.Handler
|
||||
msg = ResUI.InitialConfiguration;
|
||||
if (node.configType == EConfigType.Custom)
|
||||
{
|
||||
return GenerateClientCustomConfig(node, fileName, out msg);
|
||||
if (node.coreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo)
|
||||
{
|
||||
var configGenClash = new CoreConfigClash(config);
|
||||
return configGenClash.GenerateClientConfig(node, fileName, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GenerateClientCustomConfig(node, fileName, out msg);
|
||||
}
|
||||
}
|
||||
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
|
||||
else if (LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
|
||||
{
|
||||
var configGenSingbox = new CoreConfigSingbox(config);
|
||||
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
@@ -154,7 +163,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
if ((new CoreConfigSingbox(config)).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
if (new CoreConfigSingbox(config).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -162,7 +171,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((new CoreConfigV2ray(config)).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
|
||||
if (new CoreConfigV2ray(config).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Net;
|
||||
using System.Data;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace v2rayN.Handler.CoreConfig
|
||||
{
|
||||
internal class CoreConfigSingbox
|
||||
{
|
||||
@@ -25,6 +27,11 @@ namespace v2rayN.Handler
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp))
|
||||
{
|
||||
msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = ResUI.InitialConfiguration;
|
||||
|
||||
@@ -54,7 +61,9 @@ namespace v2rayN.Handler
|
||||
|
||||
GenDns(node, singboxConfig);
|
||||
|
||||
GenStatistic(singboxConfig);
|
||||
GenExperimental(singboxConfig);
|
||||
|
||||
ConvertGeo2Ruleset(singboxConfig);
|
||||
|
||||
msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
}
|
||||
@@ -109,9 +118,11 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
singboxConfig.inbounds.Clear();
|
||||
var listen = "::";
|
||||
singboxConfig.inbounds = [];
|
||||
|
||||
if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound))
|
||||
if (!_config.tunModeItem.enableTun
|
||||
|| (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound && _config.runningCoreType == ECoreType.sing_box))
|
||||
{
|
||||
var inbound = new Inbound4Sbox()
|
||||
{
|
||||
@@ -144,11 +155,11 @@ namespace v2rayN.Handler
|
||||
if (_config.inbound[0].newPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
||||
inbound3.listen = "::";
|
||||
inbound3.listen = listen;
|
||||
singboxConfig.inbounds.Add(inbound3);
|
||||
|
||||
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
|
||||
inbound4.listen = "::";
|
||||
inbound4.listen = listen;
|
||||
singboxConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
@@ -160,8 +171,8 @@ namespace v2rayN.Handler
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = "::";
|
||||
inbound2.listen = "::";
|
||||
inbound.listen = listen;
|
||||
inbound2.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +193,7 @@ namespace v2rayN.Handler
|
||||
tunInbound.strict_route = _config.tunModeItem.strictRoute;
|
||||
tunInbound.stack = _config.tunModeItem.stack;
|
||||
tunInbound.sniff = _config.inbound[0].sniffingEnabled;
|
||||
tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
|
||||
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
|
||||
if (_config.tunModeItem.enableIPv6Address == false)
|
||||
{
|
||||
tunInbound.inet6_address = null;
|
||||
@@ -213,105 +224,111 @@ namespace v2rayN.Handler
|
||||
{
|
||||
outbound.server = node.address;
|
||||
outbound.server_port = node.port;
|
||||
outbound.type = Global.ProtocolTypes[node.configType];
|
||||
|
||||
if (node.configType == EConfigType.VMess)
|
||||
switch (node.configType)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.VMess];
|
||||
|
||||
outbound.uuid = node.id;
|
||||
outbound.alter_id = node.alterId;
|
||||
if (Global.VmessSecurities.Contains(node.security))
|
||||
{
|
||||
outbound.security = node.security;
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
GenOutboundMux(node, outbound);
|
||||
}
|
||||
else if (node.configType == EConfigType.Shadowsocks)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Shadowsocks];
|
||||
|
||||
outbound.method = LazyConfig.Instance.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : Global.None;
|
||||
outbound.password = node.id;
|
||||
|
||||
GenOutboundMux(node, outbound);
|
||||
}
|
||||
else if (node.configType == EConfigType.Socks)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Socks];
|
||||
|
||||
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.ProtocolTypes[EConfigType.VLESS];
|
||||
|
||||
outbound.uuid = node.id;
|
||||
|
||||
outbound.packet_encoding = "xudp";
|
||||
|
||||
if (Utils.IsNullOrEmpty(node.flow))
|
||||
{
|
||||
GenOutboundMux(node, outbound);
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.flow = node.flow;
|
||||
}
|
||||
}
|
||||
else if (node.configType == EConfigType.Trojan)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Trojan];
|
||||
|
||||
outbound.password = node.id;
|
||||
|
||||
GenOutboundMux(node, outbound);
|
||||
}
|
||||
else if (node.configType == EConfigType.Hysteria2)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Hysteria2];
|
||||
|
||||
outbound.password = node.id;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
{
|
||||
outbound.obfs = new()
|
||||
case EConfigType.VMess:
|
||||
{
|
||||
type = "salamander",
|
||||
password = node.path.TrimEx(),
|
||||
};
|
||||
}
|
||||
outbound.uuid = node.id;
|
||||
outbound.alter_id = node.alterId;
|
||||
if (Global.VmessSecurities.Contains(node.security))
|
||||
{
|
||||
outbound.security = node.security;
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
outbound.up_mbps = _config.hysteriaItem.up_mbps > 0 ? _config.hysteriaItem.up_mbps : null;
|
||||
outbound.down_mbps = _config.hysteriaItem.down_mbps > 0 ? _config.hysteriaItem.down_mbps : null;
|
||||
}
|
||||
else if (node.configType == EConfigType.Tuic)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Tuic];
|
||||
GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.Shadowsocks:
|
||||
{
|
||||
outbound.method = LazyConfig.Instance.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : Global.None;
|
||||
outbound.password = node.id;
|
||||
|
||||
outbound.uuid = node.id;
|
||||
outbound.password = node.security;
|
||||
outbound.congestion_control = node.headerType;
|
||||
}
|
||||
else if (node.configType == EConfigType.Wireguard)
|
||||
{
|
||||
outbound.type = Global.ProtocolTypes[EConfigType.Wireguard];
|
||||
GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.Socks:
|
||||
{
|
||||
outbound.version = "5";
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.Http:
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.VLESS:
|
||||
{
|
||||
outbound.uuid = node.id;
|
||||
|
||||
outbound.private_key = node.id;
|
||||
outbound.peer_public_key = node.publicKey;
|
||||
outbound.reserved = Utils.String2List(node.path).Select(int.Parse).ToArray();
|
||||
outbound.local_address = [.. Utils.String2List(node.requestHost)];
|
||||
outbound.mtu = Utils.ToInt(node.shortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.shortId);
|
||||
outbound.packet_encoding = "xudp";
|
||||
|
||||
if (Utils.IsNullOrEmpty(node.flow))
|
||||
{
|
||||
GenOutboundMux(node, outbound);
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.flow = node.flow;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.Trojan:
|
||||
{
|
||||
outbound.password = node.id;
|
||||
|
||||
GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.Hysteria2:
|
||||
{
|
||||
outbound.password = node.id;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
{
|
||||
outbound.obfs = new()
|
||||
{
|
||||
type = "salamander",
|
||||
password = node.path.TrimEx(),
|
||||
};
|
||||
}
|
||||
|
||||
outbound.up_mbps = _config.hysteriaItem.up_mbps > 0 ? _config.hysteriaItem.up_mbps : null;
|
||||
outbound.down_mbps = _config.hysteriaItem.down_mbps > 0 ? _config.hysteriaItem.down_mbps : null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Tuic:
|
||||
{
|
||||
outbound.uuid = node.id;
|
||||
outbound.password = node.security;
|
||||
outbound.congestion_control = node.headerType;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Wireguard:
|
||||
{
|
||||
outbound.private_key = node.id;
|
||||
outbound.peer_public_key = node.publicKey;
|
||||
outbound.reserved = Utils.String2List(node.path).Select(int.Parse).ToArray();
|
||||
outbound.local_address = [.. Utils.String2List(node.requestHost)];
|
||||
outbound.mtu = Utils.ToInt(node.shortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.shortId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GenOutboundTls(node, outbound);
|
||||
@@ -329,16 +346,16 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
//if (_config.coreBasicItem.muxEnabled)
|
||||
//{
|
||||
// var mux = new Multiplex4Sbox()
|
||||
// {
|
||||
// enabled = true,
|
||||
// protocol = _config.mux4SboxItem.protocol,
|
||||
// max_connections = _config.mux4SboxItem.max_connections,
|
||||
// };
|
||||
// outbound.multiplex = mux;
|
||||
//}
|
||||
if (_config.coreBasicItem.muxEnabled && !Utils.IsNullOrEmpty(_config.mux4SboxItem.protocol))
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
protocol = _config.mux4SboxItem.protocol,
|
||||
max_connections = _config.mux4SboxItem.max_connections,
|
||||
};
|
||||
outbound.multiplex = mux;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -531,24 +548,38 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
var dnsOutbound = "dns_out";
|
||||
if (!_config.inbound[0].sniffingEnabled)
|
||||
{
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
port = [53],
|
||||
network = ["udp"],
|
||||
outbound = dnsOutbound
|
||||
});
|
||||
}
|
||||
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
singboxConfig.route.auto_detect_interface = true;
|
||||
|
||||
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(Utils.GetEmbedText(Global.TunSingboxRulesFileName));
|
||||
singboxConfig.route.rules.AddRange(tunRules);
|
||||
if (tunRules != null)
|
||||
{
|
||||
singboxConfig.route.rules.AddRange(tunRules);
|
||||
}
|
||||
|
||||
GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe);
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
port = new() { 53 },
|
||||
outbound = "dns_out",
|
||||
outbound = dnsOutbound,
|
||||
process_name = lstDnsExe
|
||||
});
|
||||
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = "direct",
|
||||
outbound = Global.DirectTag,
|
||||
process_name = lstDirectExe
|
||||
});
|
||||
}
|
||||
@@ -639,6 +670,10 @@ namespace v2rayN.Handler
|
||||
rule.port = new List<int> { Utils.ToInt(item.port) };
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.network))
|
||||
{
|
||||
rule.network = Utils.String2List(item.network);
|
||||
}
|
||||
if (item.protocol?.Count > 0)
|
||||
{
|
||||
rule.protocol = item.protocol;
|
||||
@@ -647,28 +682,37 @@ namespace v2rayN.Handler
|
||||
{
|
||||
rule.inbound = item.inboundTag;
|
||||
}
|
||||
var rule1 = JsonUtils.DeepCopy(rule);
|
||||
var rule2 = JsonUtils.DeepCopy(rule);
|
||||
var rule3 = JsonUtils.DeepCopy(rule);
|
||||
|
||||
var hasDomainIp = false;
|
||||
if (item.domain?.Count > 0)
|
||||
{
|
||||
var countDomain = 0;
|
||||
foreach (var it in item.domain)
|
||||
{
|
||||
ParseV2Domain(it, rule);
|
||||
if (ParseV2Domain(it, rule1)) countDomain++;
|
||||
}
|
||||
if (countDomain > 0)
|
||||
{
|
||||
rules.Add(rule1);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
rules.Add(rule);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
|
||||
if (item.ip?.Count > 0)
|
||||
{
|
||||
var countIp = 0;
|
||||
foreach (var it in item.ip)
|
||||
{
|
||||
ParseV2Address(it, rule2);
|
||||
if (ParseV2Address(it, rule2)) countIp++;
|
||||
}
|
||||
if (countIp > 0)
|
||||
{
|
||||
rules.Add(rule2);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
rules.Add(rule2);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
|
||||
if (_config.tunModeItem.enableTun && item.process?.Count > 0)
|
||||
@@ -678,7 +722,8 @@ namespace v2rayN.Handler
|
||||
hasDomainIp = true;
|
||||
}
|
||||
|
||||
if (!hasDomainIp)
|
||||
if (!hasDomainIp
|
||||
&& (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null))
|
||||
{
|
||||
rules.Add(rule);
|
||||
}
|
||||
@@ -690,11 +735,11 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ParseV2Domain(string domain, Rule4Sbox rule)
|
||||
private bool ParseV2Domain(string domain, Rule4Sbox rule)
|
||||
{
|
||||
if (domain.StartsWith("#") || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else if (domain.StartsWith("geosite:"))
|
||||
{
|
||||
@@ -728,17 +773,22 @@ namespace v2rayN.Handler
|
||||
rule.domain_keyword ??= [];
|
||||
rule.domain_keyword?.Add(domain);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ParseV2Address(string address, Rule4Sbox rule)
|
||||
private bool ParseV2Address(string address, Rule4Sbox rule)
|
||||
{
|
||||
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else if (address.StartsWith("geoip:!"))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
else if (address.Equals("geoip:private"))
|
||||
{
|
||||
rule.ip_is_private = true;
|
||||
}
|
||||
else if (address.StartsWith("geoip:"))
|
||||
{
|
||||
@@ -750,56 +800,32 @@ namespace v2rayN.Handler
|
||||
if (rule.ip_cidr is null) { rule.ip_cidr = new(); }
|
||||
rule.ip_cidr?.Add(address);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int GenDns(ProfileItem node, SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dns4Sbox? dns4Sbox;
|
||||
var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var strDNS = string.Empty;
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var tunDNS = item?.tunDNS;
|
||||
if (Utils.IsNullOrEmpty(tunDNS))
|
||||
{
|
||||
tunDNS = Utils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
}
|
||||
dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(tunDNS);
|
||||
strDNS = Utils.IsNullOrEmpty(item?.tunDNS) ? Utils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.tunDNS;
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var normalDNS = item?.normalDNS;
|
||||
if (Utils.IsNullOrEmpty(normalDNS))
|
||||
{
|
||||
normalDNS = "{\"servers\":[{\"address\":\"tcp://8.8.8.8\"}]}";
|
||||
}
|
||||
|
||||
dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(normalDNS);
|
||||
strDNS = Utils.IsNullOrEmpty(item?.normalDNS) ? Utils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.normalDNS;
|
||||
}
|
||||
|
||||
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
||||
if (dns4Sbox is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//Add the dns of the remote server domain
|
||||
if (dns4Sbox.rules is null)
|
||||
{
|
||||
dns4Sbox.rules = new();
|
||||
}
|
||||
dns4Sbox.servers.Add(new()
|
||||
{
|
||||
tag = "local_local",
|
||||
address = "223.5.5.5",
|
||||
detour = "direct"
|
||||
});
|
||||
dns4Sbox.rules.Add(new()
|
||||
{
|
||||
server = "local_local",
|
||||
outbound = "any"
|
||||
});
|
||||
|
||||
singboxConfig.dns = dns4Sbox;
|
||||
|
||||
GenDnsDomains(node, singboxConfig);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -808,30 +834,161 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenStatistic(SingboxConfig singboxConfig)
|
||||
private int GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig)
|
||||
{
|
||||
var dns4Sbox = singboxConfig.dns ?? new();
|
||||
dns4Sbox.servers ??= [];
|
||||
dns4Sbox.rules ??= [];
|
||||
|
||||
var tag = "local_local";
|
||||
dns4Sbox.servers.Add(new()
|
||||
{
|
||||
tag = tag,
|
||||
address = "223.5.5.5",
|
||||
detour = Global.DirectTag,
|
||||
//strategy = strategy
|
||||
});
|
||||
|
||||
var lstDomain = singboxConfig.outbounds
|
||||
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
|
||||
.Select(t => t.server)
|
||||
.ToList();
|
||||
if (lstDomain != null && lstDomain.Count > 0)
|
||||
{
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = tag,
|
||||
domain = lstDomain
|
||||
});
|
||||
}
|
||||
|
||||
//Tun2SocksAddress
|
||||
if (_config.tunModeItem.enableTun && node?.configType == EConfigType.Socks && Utils.IsDomain(node?.sni))
|
||||
{
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = tag,
|
||||
domain = [node?.sni]
|
||||
});
|
||||
}
|
||||
|
||||
singboxConfig.dns = dns4Sbox;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenExperimental(SingboxConfig singboxConfig)
|
||||
{
|
||||
if (_config.guiItem.enableStatistics)
|
||||
{
|
||||
singboxConfig.experimental = new Experimental4Sbox()
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||
{
|
||||
//cache_file = new CacheFile4Sbox()
|
||||
//{
|
||||
// enabled = true
|
||||
//},
|
||||
//v2ray_api = new V2ray_Api4Sbox()
|
||||
//{
|
||||
// listen = $"{Global.Loopback}:{Global.StatePort}",
|
||||
// stats = new Stats4Sbox()
|
||||
// {
|
||||
// enabled = true,
|
||||
// }
|
||||
//},
|
||||
clash_api = new Clash_Api4Sbox()
|
||||
{
|
||||
external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort}",
|
||||
}
|
||||
external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}",
|
||||
};
|
||||
}
|
||||
|
||||
if (_config.coreBasicItem.enableCacheFile4Sbox)
|
||||
{
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.cache_file = new CacheFile4Sbox()
|
||||
{
|
||||
enabled = true
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int ConvertGeo2Ruleset(SingboxConfig singboxConfig)
|
||||
{
|
||||
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
||||
{
|
||||
if (rule_set != null) ruleSets.AddRange(rule_set);
|
||||
}
|
||||
var geosite = "geosite";
|
||||
var geoip = "geoip";
|
||||
var ruleSets = new List<string>();
|
||||
|
||||
//convert route geosite & geoip to ruleset
|
||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList();
|
||||
rule.geosite = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList();
|
||||
rule.geoip = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
|
||||
//convert dns geosite & geoip to ruleset
|
||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList();
|
||||
rule.geosite = null;
|
||||
}
|
||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList();
|
||||
rule.geoip = null;
|
||||
}
|
||||
foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
AddRuleSets(ruleSets, dnsRule.rule_set);
|
||||
}
|
||||
//rules in rules
|
||||
foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
||||
{
|
||||
foreach (var item2 in item ?? [])
|
||||
{
|
||||
AddRuleSets(ruleSets, item2.rule_set);
|
||||
}
|
||||
}
|
||||
|
||||
//load custom ruleset file
|
||||
List<Ruleset4Sbox> customRulesets = [];
|
||||
if (_config.routingBasicItem.enableRoutingAdvanced)
|
||||
{
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (!Utils.IsNullOrEmpty(routing.customRulesetPath4Singbox))
|
||||
{
|
||||
var result = Utils.LoadResource(routing.customRulesetPath4Singbox);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
||||
.Where(t => t.tag != null)
|
||||
.Where(t => t.type != null)
|
||||
.Where(t => t.format != null)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add ruleset srs
|
||||
singboxConfig.route.rule_set = [];
|
||||
foreach (var item in new HashSet<string>(ruleSets))
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(item)) { continue; }
|
||||
var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item));
|
||||
if (customRuleset != null)
|
||||
{
|
||||
singboxConfig.route.rule_set.Add(customRuleset);
|
||||
}
|
||||
else
|
||||
{
|
||||
singboxConfig.route.rule_set.Add(new()
|
||||
{
|
||||
type = "remote",
|
||||
format = "binary",
|
||||
tag = item,
|
||||
url = string.Format(Global.SingboxRulesetUrl, item.StartsWith(geosite) ? geosite : geoip, item),
|
||||
download_detour = Global.ProxyTag
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -972,12 +1129,18 @@ namespace v2rayN.Handler
|
||||
singboxConfig.route.rules.Add(rule);
|
||||
}
|
||||
|
||||
GenDns(new(), singboxConfig);
|
||||
var dnsServer = singboxConfig.dns?.servers.FirstOrDefault();
|
||||
if (dnsServer != null)
|
||||
{
|
||||
dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound;
|
||||
}
|
||||
GenDnsDomains(null, singboxConfig);
|
||||
//var dnsServer = singboxConfig.dns?.servers.FirstOrDefault();
|
||||
//if (dnsServer != null)
|
||||
//{
|
||||
// dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound;
|
||||
//}
|
||||
//var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault();
|
||||
//if (dnsRule != null)
|
||||
//{
|
||||
// singboxConfig.dns.rules = [];
|
||||
// singboxConfig.dns.rules.Add(dnsRule);
|
||||
//}
|
||||
|
||||
//msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
||||
return 0;
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text.Json.Nodes;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace v2rayN.Handler.CoreConfig
|
||||
{
|
||||
internal class CoreConfigV2ray
|
||||
{
|
||||
@@ -52,7 +54,7 @@ namespace v2rayN.Handler
|
||||
|
||||
GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
GenDns(v2rayConfig);
|
||||
GenDns(node, v2rayConfig);
|
||||
|
||||
GenStatistic(v2rayConfig);
|
||||
|
||||
@@ -98,7 +100,8 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
v2rayConfig.inbounds = new List<Inbounds4Ray>();
|
||||
var listen = "0.0.0.0";
|
||||
v2rayConfig.inbounds = [];
|
||||
|
||||
Inbounds4Ray? inbound = GetInbound(_config.inbound[0], EInboundProtocol.socks, true);
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
@@ -112,11 +115,11 @@ namespace v2rayN.Handler
|
||||
if (_config.inbound[0].newPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(_config.inbound[0], EInboundProtocol.socks2, true);
|
||||
inbound3.listen = "0.0.0.0";
|
||||
inbound3.listen = listen;
|
||||
v2rayConfig.inbounds.Add(inbound3);
|
||||
|
||||
var inbound4 = GetInbound(_config.inbound[0], EInboundProtocol.http2, false);
|
||||
inbound4.listen = "0.0.0.0";
|
||||
inbound4.listen = listen;
|
||||
v2rayConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
@@ -131,8 +134,8 @@ namespace v2rayN.Handler
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = "0.0.0.0";
|
||||
inbound2.listen = "0.0.0.0";
|
||||
inbound.listen = listen;
|
||||
inbound2.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,24 +146,25 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Inbounds4Ray? GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||
{
|
||||
string result = Utils.GetEmbedText(Global.V2raySampleInbound);
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
return null;
|
||||
return new();
|
||||
}
|
||||
|
||||
var inbound = JsonUtils.Deserialize<Inbounds4Ray>(result);
|
||||
if (inbound == null)
|
||||
{
|
||||
return null;
|
||||
return new();
|
||||
}
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.port = inItem.localPort + (int)protocol;
|
||||
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
|
||||
inbound.settings.udp = inItem.udpEnabled;
|
||||
inbound.sniffing.enabled = inItem.sniffingEnabled;
|
||||
inbound.sniffing.destOverride = inItem.destOverride;
|
||||
inbound.sniffing.routeOnly = inItem.routeOnly;
|
||||
|
||||
return inbound;
|
||||
@@ -217,39 +221,43 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenRoutingUserRule(RulesItem4Ray? rules, V2rayConfig v2rayConfig)
|
||||
private int GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (rules == null)
|
||||
if (rule == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(rules.port))
|
||||
if (Utils.IsNullOrEmpty(rule.port))
|
||||
{
|
||||
rules.port = null;
|
||||
rule.port = null;
|
||||
}
|
||||
if (rules.domain?.Count == 0)
|
||||
if (Utils.IsNullOrEmpty(rule.network))
|
||||
{
|
||||
rules.domain = null;
|
||||
rule.network = null;
|
||||
}
|
||||
if (rules.ip?.Count == 0)
|
||||
if (rule.domain?.Count == 0)
|
||||
{
|
||||
rules.ip = null;
|
||||
rule.domain = null;
|
||||
}
|
||||
if (rules.protocol?.Count == 0)
|
||||
if (rule.ip?.Count == 0)
|
||||
{
|
||||
rules.protocol = null;
|
||||
rule.ip = null;
|
||||
}
|
||||
if (rules.inboundTag?.Count == 0)
|
||||
if (rule.protocol?.Count == 0)
|
||||
{
|
||||
rules.inboundTag = null;
|
||||
rule.protocol = null;
|
||||
}
|
||||
if (rule.inboundTag?.Count == 0)
|
||||
{
|
||||
rule.inboundTag = null;
|
||||
}
|
||||
|
||||
var hasDomainIp = false;
|
||||
if (rules.domain?.Count > 0)
|
||||
if (rule.domain?.Count > 0)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rules);
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.ip = null;
|
||||
it.type = "field";
|
||||
for (int k = it.domain.Count - 1; k >= 0; k--)
|
||||
@@ -263,9 +271,9 @@ namespace v2rayN.Handler
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
if (rules.ip?.Count > 0)
|
||||
if (rule.ip?.Count > 0)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rules);
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.domain = null;
|
||||
it.type = "field";
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
@@ -273,12 +281,12 @@ namespace v2rayN.Handler
|
||||
}
|
||||
if (!hasDomainIp)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(rules.port)
|
||||
|| (rules.protocol?.Count > 0)
|
||||
|| (rules.inboundTag?.Count > 0)
|
||||
if (!Utils.IsNullOrEmpty(rule.port)
|
||||
|| rule.protocol?.Count > 0
|
||||
|| rule.inboundTag?.Count > 0
|
||||
)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rules);
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.type = "field";
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
}
|
||||
@@ -295,182 +303,188 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
if (node.configType == EConfigType.VMess)
|
||||
switch (node.configType)
|
||||
{
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
}
|
||||
vnextItem.address = node.address;
|
||||
vnextItem.port = node.port;
|
||||
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
}
|
||||
//远程服务器用户ID
|
||||
usersItem.id = node.id;
|
||||
usersItem.alterId = node.alterId;
|
||||
usersItem.email = Global.UserEMail;
|
||||
if (Global.VmessSecurities.Contains(node.security))
|
||||
{
|
||||
usersItem.security = node.security;
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[EConfigType.VMess];
|
||||
outbound.settings.servers = null;
|
||||
}
|
||||
else if (node.configType == EConfigType.Shadowsocks)
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
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.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : "none";
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[EConfigType.Shadowsocks];
|
||||
outbound.settings.vnext = null;
|
||||
}
|
||||
else if (node.configType == EConfigType.Socks)
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
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))
|
||||
{
|
||||
SocksUsersItem4Ray socksUsersItem = new()
|
||||
case EConfigType.VMess:
|
||||
{
|
||||
user = node.security,
|
||||
pass = node.id,
|
||||
level = 1
|
||||
};
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
}
|
||||
vnextItem.address = node.address;
|
||||
vnextItem.port = node.port;
|
||||
|
||||
serversItem.users = new List<SocksUsersItem4Ray>() { socksUsersItem };
|
||||
}
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
}
|
||||
//远程服务器用户ID
|
||||
usersItem.id = node.id;
|
||||
usersItem.alterId = node.alterId;
|
||||
usersItem.email = Global.UserEMail;
|
||||
if (Global.VmessSecurities.Contains(node.security))
|
||||
{
|
||||
usersItem.security = node.security;
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[EConfigType.Socks];
|
||||
outbound.settings.vnext = null;
|
||||
}
|
||||
else if (node.configType == EConfigType.VLESS)
|
||||
{
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
}
|
||||
vnextItem.address = node.address;
|
||||
vnextItem.port = node.port;
|
||||
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
}
|
||||
usersItem.id = node.id;
|
||||
usersItem.email = Global.UserEMail;
|
||||
usersItem.encryption = node.security;
|
||||
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
|
||||
if (node.streamSecurity == Global.StreamSecurityReality
|
||||
|| node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.flow))
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Shadowsocks:
|
||||
{
|
||||
usersItem.flow = node.flow;
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
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.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : "none";
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node.streamSecurity == Global.StreamSecurityReality && Utils.IsNullOrEmpty(node.flow))
|
||||
{
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
}
|
||||
case EConfigType.Socks:
|
||||
case EConfigType.Http:
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
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;
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[EConfigType.VLESS];
|
||||
outbound.settings.servers = null;
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
{
|
||||
SocksUsersItem4Ray socksUsersItem = new()
|
||||
{
|
||||
user = node.security,
|
||||
pass = node.id,
|
||||
level = 1
|
||||
};
|
||||
|
||||
serversItem.users = new List<SocksUsersItem4Ray>() { socksUsersItem };
|
||||
}
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.VLESS:
|
||||
{
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext?.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext[0];
|
||||
}
|
||||
vnextItem.address = node.address;
|
||||
vnextItem.port = node.port;
|
||||
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users[0];
|
||||
}
|
||||
usersItem.id = node.id;
|
||||
usersItem.email = Global.UserEMail;
|
||||
usersItem.encryption = node.security;
|
||||
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
|
||||
if (node.streamSecurity == Global.StreamSecurityReality
|
||||
|| node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.flow))
|
||||
{
|
||||
usersItem.flow = node.flow;
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
}
|
||||
}
|
||||
if (node.streamSecurity == Global.StreamSecurityReality && Utils.IsNullOrEmpty(node.flow))
|
||||
{
|
||||
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
|
||||
}
|
||||
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Trojan:
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
outbound.settings.servers.Add(serversItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers[0];
|
||||
}
|
||||
serversItem.address = node.address;
|
||||
serversItem.port = node.port;
|
||||
serversItem.password = node.id;
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (node.configType == EConfigType.Trojan)
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
outbound.settings.servers.Add(serversItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers[0];
|
||||
}
|
||||
serversItem.address = node.address;
|
||||
serversItem.port = node.port;
|
||||
serversItem.password = node.id;
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
GenOutboundMux(node, outbound, false);
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[EConfigType.Trojan];
|
||||
outbound.settings.vnext = null;
|
||||
}
|
||||
outbound.protocol = Global.ProtocolTypes[node.configType];
|
||||
GenBoundStreamSettings(node, outbound.streamSettings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -556,6 +570,7 @@ namespace v2rayN.Handler
|
||||
publicKey = node.publicKey,
|
||||
shortId = node.shortId,
|
||||
spiderX = node.spiderX,
|
||||
show = false,
|
||||
};
|
||||
|
||||
streamSettings.realitySettings = realitySettings;
|
||||
@@ -621,6 +636,25 @@ namespace v2rayN.Handler
|
||||
}
|
||||
streamSettings.httpupgradeSettings = httpupgradeSettings;
|
||||
|
||||
break;
|
||||
//splithttp
|
||||
case nameof(ETransport.splithttp):
|
||||
SplithttpSettings4Ray splithttpSettings = new()
|
||||
{
|
||||
maxUploadSize = 1000000,
|
||||
maxConcurrentUploads = 10
|
||||
};
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
{
|
||||
splithttpSettings.path = node.path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
{
|
||||
splithttpSettings.host = host;
|
||||
}
|
||||
streamSettings.splithttpSettings = splithttpSettings;
|
||||
|
||||
break;
|
||||
//h2
|
||||
case nameof(ETransport.h2):
|
||||
@@ -665,7 +699,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
authority = Utils.IsNullOrEmpty(host) ? null : host,
|
||||
serviceName = node.path,
|
||||
multiMode = (node.headerType == Global.GrpcMultiMode),
|
||||
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,
|
||||
@@ -715,7 +749,7 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenDns(V2rayConfig v2rayConfig)
|
||||
private int GenDns(ProfileItem node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -724,7 +758,7 @@ namespace v2rayN.Handler
|
||||
var domainStrategy4Freedom = item?.domainStrategy4Freedom;
|
||||
if (Utils.IsNullOrEmpty(normalDNS))
|
||||
{
|
||||
normalDNS = "1.1.1.1,8.8.8.8";
|
||||
normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
}
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
@@ -767,6 +801,8 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
GenDnsDomains(node, obj);
|
||||
|
||||
v2rayConfig.dns = obj;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -776,6 +812,24 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenDnsDomains(ProfileItem node, JsonNode dns)
|
||||
{
|
||||
var servers = dns["servers"];
|
||||
if (servers != null)
|
||||
{
|
||||
if (Utils.IsDomain(node.address))
|
||||
{
|
||||
var dnsServer = new DnsServer4Ray()
|
||||
{
|
||||
address = "223.5.5.5",
|
||||
domains = [node.address]
|
||||
};
|
||||
servers.AsArray().Insert(0, JsonUtils.SerializeToNode(dnsServer));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GenStatistic(V2rayConfig v2rayConfig)
|
||||
{
|
||||
if (_config.guiItem.enableStatistics)
|
||||
@@ -828,6 +882,33 @@ namespace v2rayN.Handler
|
||||
|
||||
private int GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
//fragment proxy
|
||||
if (_config.coreBasicItem.enableFragment
|
||||
&& !Utils.IsNullOrEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
{
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
protocol = "freedom",
|
||||
tag = $"{Global.ProxyTag}3",
|
||||
settings = new()
|
||||
{
|
||||
fragment = new()
|
||||
{
|
||||
packets = "tlshello",
|
||||
length = "100-200",
|
||||
interval = "10-20"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
v2rayConfig.outbounds.Add(fragmentOutbound);
|
||||
v2rayConfig.outbounds[0].streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = fragmentOutbound.tag
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node.subid.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler.CoreConfig;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
@@ -39,6 +40,7 @@ namespace v2rayN.Handler
|
||||
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -54,22 +56,22 @@ namespace v2rayN.Handler
|
||||
CoreStart(node);
|
||||
|
||||
//In tun mode, do a delay check and restart the core
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
Observable.Range(1, 1)
|
||||
.Delay(TimeSpan.FromSeconds(15))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
{
|
||||
if (_process == null || _process.HasExited)
|
||||
{
|
||||
CoreStart(node);
|
||||
ShowMsg(false, "Tun mode restart the core once");
|
||||
Logging.SaveLog("Tun mode restart the core once");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//if (_config.tunModeItem.enableTun)
|
||||
//{
|
||||
// Observable.Range(1, 1)
|
||||
// .Delay(TimeSpan.FromSeconds(15))
|
||||
// .Subscribe(x =>
|
||||
// {
|
||||
// {
|
||||
// if (_process == null || _process.HasExited)
|
||||
// {
|
||||
// CoreStart(node);
|
||||
// ShowMsg(false, "Tun mode restart the core once");
|
||||
// Logging.SaveLog("Tun mode restart the core once");
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,15 +185,16 @@ namespace v2rayN.Handler
|
||||
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
|
||||
ECoreType coreType;
|
||||
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
|
||||
{
|
||||
coreType = ECoreType.sing_box;
|
||||
}
|
||||
else
|
||||
{
|
||||
coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
}
|
||||
//ECoreType coreType;
|
||||
//if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
|
||||
//{
|
||||
// coreType = ECoreType.sing_box;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
//}
|
||||
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
_config.runningCoreType = coreType;
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
|
||||
|
||||
@@ -203,23 +206,40 @@ namespace v2rayN.Handler
|
||||
}
|
||||
_process = proc;
|
||||
|
||||
//start a socks service
|
||||
//start a pre service
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
|
||||
ProfileItem? itemSocks = null;
|
||||
var preCoreType = ECoreType.sing_box;
|
||||
if (node.configType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.tunModeItem.enableTun)
|
||||
{
|
||||
var itemSocks = new ProfileItem()
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
coreType = ECoreType.sing_box,
|
||||
coreType = preCoreType,
|
||||
configType = EConfigType.Socks,
|
||||
address = Global.Loopback,
|
||||
sni = node.address, //Tun2SocksAddress
|
||||
port = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
};
|
||||
}
|
||||
else if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
|
||||
{
|
||||
preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
coreType = preCoreType,
|
||||
configType = EConfigType.Socks,
|
||||
address = Global.Loopback,
|
||||
port = node.preSocksPort
|
||||
};
|
||||
_config.runningCoreType = ECoreType.sing_box;
|
||||
_config.runningCoreType = preCoreType;
|
||||
}
|
||||
if (itemSocks != null)
|
||||
{
|
||||
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
|
||||
{
|
||||
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box);
|
||||
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(preCoreType);
|
||||
var proc2 = RunProcess(node, coreInfo2, $" -c {Global.CorePreConfigFileName}", true);
|
||||
if (proc2 is not null)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Sockets;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
|
||||
203
v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs
Normal file
203
v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class BaseFmt
|
||||
{
|
||||
protected static string GetIpv6(string address)
|
||||
{
|
||||
if (Utils.IsIpv6(address))
|
||||
{
|
||||
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
|
||||
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
|
||||
}
|
||||
return address; // 如果不是IPv6地址,直接返回原地址
|
||||
}
|
||||
|
||||
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(item.flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.flow);
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.streamSecurity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (securityDef != null)
|
||||
{
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.spiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
|
||||
}
|
||||
|
||||
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
|
||||
if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode)
|
||||
{
|
||||
dicQuery.Add("mode", Utils.UrlEncode(item.headerType));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
|
||||
{
|
||||
item.flow = query["flow"] ?? "";
|
||||
item.streamSecurity = query["security"] ?? "";
|
||||
item.sni = query["sni"] ?? "";
|
||||
item.alpn = Utils.UrlDecode(query["alpn"] ?? "");
|
||||
item.fingerprint = Utils.UrlDecode(query["fp"] ?? "");
|
||||
item.publicKey = Utils.UrlDecode(query["pbk"] ?? "");
|
||||
item.shortId = Utils.UrlDecode(query["sid"] ?? "");
|
||||
item.spiderX = Utils.UrlDecode(query["spx"] ?? "");
|
||||
|
||||
item.network = query["type"] ?? nameof(ETransport.tcp);
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.path = Utils.UrlDecode(query["seed"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
item.network = nameof(ETransport.h2);
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.requestHost = query["quicSecurity"] ?? Global.None;
|
||||
item.path = Utils.UrlDecode(query["key"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
item.requestHost = Utils.UrlDecode(query["authority"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["serviceName"] ?? "");
|
||||
item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static bool Contains(string str, params string[] s)
|
||||
{
|
||||
foreach (var item in s)
|
||||
{
|
||||
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static string WriteAllText(string strData, string ext = "json")
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.{ext}");
|
||||
File.WriteAllText(fileName, strData);
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
v2rayN/v2rayN/Handler/Fmt/ClashFmt.cs
Normal file
26
v2rayN/v2rayN/Handler/Fmt/ClashFmt.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class ClashFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "port", "socks-port", "proxies"))
|
||||
{
|
||||
var fileName = WriteAllText(strData, "yaml");
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.mihomo,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "clash_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs
Normal file
94
v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class FmtHandler
|
||||
{
|
||||
public static string? GetShareUri(ProfileItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = item.configType switch
|
||||
{
|
||||
EConfigType.VMess => VmessFmt.ToUri(item),
|
||||
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
|
||||
EConfigType.Socks => SocksFmt.ToUri(item),
|
||||
EConfigType.Trojan => TrojanFmt.ToUri(item),
|
||||
EConfigType.VLESS => VLESSFmt.ToUri(item),
|
||||
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
|
||||
EConfigType.Tuic => TuicFmt.ToUri(item),
|
||||
EConfigType.Wireguard => WireguardFmt.ToUri(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveConfig(string config, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
try
|
||||
{
|
||||
string str = config.TrimEx();
|
||||
if (Utils.IsNullOrEmpty(str))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
|
||||
{
|
||||
return VmessFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
|
||||
{
|
||||
return ShadowsocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Socks]))
|
||||
{
|
||||
return SocksFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
|
||||
{
|
||||
return TrojanFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
|
||||
{
|
||||
return VLESSFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
|
||||
{
|
||||
return Hysteria2Fmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Tuic]))
|
||||
{
|
||||
return TuicFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Wireguard]))
|
||||
{
|
||||
return WireguardFmt.Resolve(str, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = ResUI.NonvmessOrssProtocol;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
msg = ResUI.Incorrectconfiguration;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs
Normal file
104
v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class Hysteria2Fmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Hysteria2
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.path = Utils.UrlDecode(query["obfs-password"] ?? "");
|
||||
item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
|
||||
}
|
||||
dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0");
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.hysteria,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "hysteria_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.hysteria2,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "hysteria2_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
v2rayN/v2rayN/Handler/Fmt/NaiveproxyFmt.cs
Normal file
26
v2rayN/v2rayN/Handler/Fmt/NaiveproxyFmt.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class NaiveproxyFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.naiveproxy,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "naiveproxy_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
183
v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs
Normal file
183
v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class ShadowsocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.address.Length == 0 || item.port == 0 || item.security.Length == 0 || item.id.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.configType = EConfigType.Shadowsocks;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new Sip002
|
||||
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static ProfileItem? ResolveSSLegacy(string result)
|
||||
{
|
||||
var match = UrlFinder.Match(result);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
||||
ProfileItem item = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (!Utils.IsNullOrEmpty(tag))
|
||||
{
|
||||
item.remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
Match details;
|
||||
try
|
||||
{
|
||||
details = DetailsParser.Match(Utils.Base64Decode(base64));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!details.Success)
|
||||
return null;
|
||||
item.security = details.Groups["method"].Value;
|
||||
item.id = details.Groups["password"].Value;
|
||||
item.address = details.Groups["hostname"].Value;
|
||||
item.port = Utils.ToInt(details.Groups["port"].Value);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSip002(string result)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem item = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped);
|
||||
//2022-blake3
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.security = userInfoParts[0];
|
||||
item.id = Utils.UrlDecode(userInfoParts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// parse base64 UserInfo
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.security = userInfoParts[0];
|
||||
item.id = userInfoParts[1];
|
||||
}
|
||||
|
||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||
if (queryParameters["plugin"] != null)
|
||||
{
|
||||
//obfs-host exists
|
||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
item.network = Global.DefaultNetwork;
|
||||
item.headerType = Global.TcpHeaderHttp;
|
||||
item.requestHost = obfsHost ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static List<ProfileItem>? ResolveSip008(string result)
|
||||
{
|
||||
//SsSIP008
|
||||
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
|
||||
if (lstSsServer?.Count <= 0)
|
||||
{
|
||||
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
|
||||
if (ssSIP008?.servers?.Count > 0)
|
||||
{
|
||||
lstSsServer = ssSIP008.servers;
|
||||
}
|
||||
}
|
||||
|
||||
if (lstSsServer?.Count > 0)
|
||||
{
|
||||
List<ProfileItem> lst = [];
|
||||
foreach (var it in lstSsServer)
|
||||
{
|
||||
var ssItem = new ProfileItem()
|
||||
{
|
||||
remarks = it.remarks,
|
||||
security = it.method,
|
||||
id = it.password,
|
||||
address = it.server,
|
||||
port = Utils.ToInt(it.server_port)
|
||||
};
|
||||
lst.Add(ssItem);
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
v2rayN/v2rayN/Handler/Fmt/SingboxFmt.cs
Normal file
58
v2rayN/v2rayN/Handler/Fmt/SingboxFmt.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class SingboxFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
|
||||
if (singboxCon?.inbounds?.Count > 0
|
||||
&& singboxCon.outbounds?.Count > 0
|
||||
&& singboxCon.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.sing_box,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "singbox_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
|
||||
if (singboxConfig?.inbounds?.Count > 0
|
||||
&& singboxConfig.outbounds?.Count > 0
|
||||
&& singboxConfig.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.sing_box,
|
||||
address = fileName,
|
||||
remarks = subRemarks ?? "singbox_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs
Normal file
131
v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class SocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.address.Length == 0 || item.port == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.configType = EConfigType.Socks;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new
|
||||
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocks(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Socks
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.Socks].Length..];
|
||||
//remark
|
||||
int indexRemark = result.IndexOf("#");
|
||||
if (indexRemark > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
|
||||
}
|
||||
catch { }
|
||||
result = result[..indexRemark];
|
||||
}
|
||||
//part decode
|
||||
int indexS = result.IndexOf("@");
|
||||
if (indexS > 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
//string[] arr22 = arr1[1].Split(':');
|
||||
int indexPort = arr1[1].LastIndexOf(":");
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.address = arr1[1][..indexPort];
|
||||
item.port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||
item.security = arr21[0];
|
||||
item.id = arr21[1];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocksNew(string result)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem item = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
|
||||
// parse base64 UserInfo
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.security = userInfoParts[0];
|
||||
item.id = userInfoParts[1];
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs
Normal file
53
v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class TrojanFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Trojan
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
GetStdTransport(item, null, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs
Normal file
68
v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class TuicFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Tuic
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.id = userInfoParts[0];
|
||||
item.security = userInfoParts[1];
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.headerType = query["congestion_control"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.headerType);
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
$"{item.id}:{item.security}",
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
v2rayN/v2rayN/Handler/Fmt/V2rayFmt.cs
Normal file
59
v2rayN/v2rayN/Handler/Fmt/V2rayFmt.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class V2rayFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
|
||||
if (v2rayCon?.inbounds?.Count > 0
|
||||
&& v2rayCon.outbounds?.Count > 0
|
||||
&& v2rayCon.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.Xray,
|
||||
address = fileName,
|
||||
remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
|
||||
if (v2rayConfig?.inbounds?.Count > 0
|
||||
&& v2rayConfig.outbounds?.Count > 0
|
||||
&& v2rayConfig.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
coreType = ECoreType.Xray,
|
||||
address = fileName,
|
||||
remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs
Normal file
64
v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class VLESSFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.VLESS,
|
||||
security = Global.None
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
item.security = query["encryption"] ?? Global.None;
|
||||
item.streamSecurity = query["security"] ?? "";
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.security);
|
||||
}
|
||||
else
|
||||
{
|
||||
dicQuery.Add("encryption", Global.None);
|
||||
}
|
||||
GetStdTransport(item, Global.None, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
229
v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs
Normal file
229
v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class VmessFmt : BaseFmt
|
||||
{
|
||||
private static readonly Regex StdVmessUserInfo = new(
|
||||
@"^(?<network>[a-z]+)(\+(?<streamSecurity>[a-z]+))?:(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled);
|
||||
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
int indexSplit = str.IndexOf("?");
|
||||
if (indexSplit > 0)
|
||||
{
|
||||
item = ResolveStdVmess(str) ?? ResolveVmess4Kitsunebi(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = ResolveVmess(str, out msg);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
VmessQRCode vmessQRCode = new()
|
||||
{
|
||||
v = item.configVersion,
|
||||
ps = item.remarks.TrimEx(),
|
||||
add = item.address,
|
||||
port = item.port,
|
||||
id = item.id,
|
||||
aid = item.alterId,
|
||||
scy = item.security,
|
||||
net = item.network,
|
||||
type = item.headerType,
|
||||
host = item.requestHost,
|
||||
path = item.path,
|
||||
tls = item.streamSecurity,
|
||||
sni = item.sni,
|
||||
alpn = item.alpn,
|
||||
fp = item.fingerprint
|
||||
};
|
||||
|
||||
url = JsonUtils.Serialize(vmessQRCode);
|
||||
url = Utils.Base64Encode(url);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveVmess(string result, out string msg)
|
||||
{
|
||||
msg = string.Empty;
|
||||
var item = new ProfileItem
|
||||
{
|
||||
configType = EConfigType.VMess
|
||||
};
|
||||
|
||||
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
|
||||
result = Utils.Base64Decode(result);
|
||||
|
||||
//转成Json
|
||||
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
|
||||
if (vmessQRCode == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
item.network = Global.DefaultNetwork;
|
||||
item.headerType = Global.None;
|
||||
|
||||
item.configVersion = Utils.ToInt(vmessQRCode.v);
|
||||
item.remarks = Utils.ToString(vmessQRCode.ps);
|
||||
item.address = Utils.ToString(vmessQRCode.add);
|
||||
item.port = Utils.ToInt(vmessQRCode.port);
|
||||
item.id = Utils.ToString(vmessQRCode.id);
|
||||
item.alterId = Utils.ToInt(vmessQRCode.aid);
|
||||
item.security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.network = vmessQRCode.net;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
|
||||
{
|
||||
item.headerType = vmessQRCode.type;
|
||||
}
|
||||
|
||||
item.requestHost = Utils.ToString(vmessQRCode.host);
|
||||
item.path = Utils.ToString(vmessQRCode.path);
|
||||
item.streamSecurity = Utils.ToString(vmessQRCode.tls);
|
||||
item.sni = Utils.ToString(vmessQRCode.sni);
|
||||
item.alpn = Utils.ToString(vmessQRCode.alpn);
|
||||
item.fingerprint = Utils.ToString(vmessQRCode.fp);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveStdVmess(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.VMess,
|
||||
security = "auto"
|
||||
};
|
||||
|
||||
Uri u = new(result);
|
||||
|
||||
item.address = u.IdnHost;
|
||||
item.port = u.Port;
|
||||
item.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var query = Utils.ParseQueryString(u.Query);
|
||||
|
||||
var m = StdVmessUserInfo.Match(u.UserInfo);
|
||||
if (!m.Success) return null;
|
||||
|
||||
item.id = m.Groups["id"].Value;
|
||||
|
||||
if (m.Groups["streamSecurity"].Success)
|
||||
{
|
||||
item.streamSecurity = m.Groups["streamSecurity"].Value;
|
||||
}
|
||||
switch (item.streamSecurity)
|
||||
{
|
||||
case Global.StreamSecurity:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
|
||||
item.network = m.Groups["network"].Value;
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
string t1 = query["type"] ?? Global.None;
|
||||
item.headerType = t1;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
item.headerType = query["type"] ?? Global.None;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
string p1 = query["path"] ?? "/";
|
||||
string h1 = query["host"] ?? "";
|
||||
item.requestHost = Utils.UrlDecode(h1);
|
||||
item.path = p1;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
item.network = nameof(ETransport.h2);
|
||||
string p2 = query["path"] ?? "/";
|
||||
string h2 = query["host"] ?? "";
|
||||
item.requestHost = Utils.UrlDecode(h2);
|
||||
item.path = p2;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
string s = query["security"] ?? Global.None;
|
||||
string k = query["key"] ?? "";
|
||||
string t3 = query["type"] ?? Global.None;
|
||||
item.headerType = t3;
|
||||
item.requestHost = Utils.UrlDecode(s);
|
||||
item.path = k;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveVmess4Kitsunebi(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.VMess
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
|
||||
int indexSplit = result.IndexOf("?");
|
||||
if (indexSplit > 0)
|
||||
{
|
||||
result = result[..indexSplit];
|
||||
}
|
||||
result = Utils.Base64Decode(result);
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
string[] arr22 = arr1[1].Split(':');
|
||||
if (arr21.Length != 2 || arr22.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.address = arr22[0];
|
||||
item.port = Utils.ToInt(arr22[1]);
|
||||
item.security = arr21[0];
|
||||
item.id = arr21[1];
|
||||
|
||||
item.network = Global.DefaultNetwork;
|
||||
item.headerType = Global.None;
|
||||
item.remarks = "Alien";
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs
Normal file
73
v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler.Fmt
|
||||
{
|
||||
internal class WireguardFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Wireguard
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
|
||||
item.publicKey = Utils.UrlDecode(query["publickey"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["reserved"] ?? "");
|
||||
item.requestHost = Utils.UrlDecode(query["address"] ?? "");
|
||||
item.shortId = Utils.UrlDecode(query["mtu"] ?? "");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
Utils.UrlEncode(item.id),
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
@@ -12,20 +13,26 @@ namespace v2rayN.Handler
|
||||
public static LazyConfig Instance => _instance.Value;
|
||||
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
|
||||
public int StatePort
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_statePort is null)
|
||||
{
|
||||
_statePort = Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
|
||||
}
|
||||
|
||||
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
|
||||
return _statePort.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public int StatePort2
|
||||
{
|
||||
get
|
||||
{
|
||||
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
||||
return _statePort2.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private Job _processJob = new();
|
||||
|
||||
public LazyConfig()
|
||||
@@ -329,7 +336,11 @@ namespace v2rayN.Handler
|
||||
arguments = "-f config.json",
|
||||
coreUrl = Global.MihomoCoreUrl,
|
||||
coreReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
coreDownloadUrl32 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-386-{0}.zip",
|
||||
coreDownloadUrl64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
|
||||
coreDownloadUrlArm64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
||||
match = "Mihomo",
|
||||
versionArg = "-v",
|
||||
redirectInfo = true,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ using Splat;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler.CoreConfig;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace v2rayN.Handler
|
||||
internal class ProfileExHandler
|
||||
{
|
||||
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
||||
private ConcurrentBag<ProfileExItem> _lstProfileEx;
|
||||
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
|
||||
private Queue<string> _queIndexIds = new();
|
||||
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
|
||||
public static ProfileExHandler Instance => _instance.Value;
|
||||
@@ -15,6 +15,15 @@ namespace v2rayN.Handler
|
||||
public ProfileExHandler()
|
||||
{
|
||||
Init();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
SaveQueueIndexIds();
|
||||
await Task.Delay(1000 * 600);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Init()
|
||||
@@ -22,24 +31,6 @@ namespace v2rayN.Handler
|
||||
SQLiteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
_lstProfileEx = new(SQLiteHelper.Instance.Table<ProfileExItem>());
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var id = _queIndexIds.Dequeue();
|
||||
var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
|
||||
if (item is not null)
|
||||
{
|
||||
SQLiteHelper.Instance.Replace(item);
|
||||
}
|
||||
}
|
||||
await Task.Delay(1000 * 60);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void IndexIdEnqueue(string indexId)
|
||||
@@ -50,6 +41,49 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveQueueIndexIds()
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
if (cnt > 0)
|
||||
{
|
||||
var lstExists = SQLiteHelper.Instance.Table<ProfileExItem>();
|
||||
List<ProfileExItem> lstInserts = [];
|
||||
List<ProfileExItem> lstUpdates = [];
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var id = _queIndexIds.Dequeue();
|
||||
var item = lstExists.FirstOrDefault(t => t.indexId == id);
|
||||
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.indexId == id);
|
||||
if (itemNew is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
lstUpdates.Add(itemNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
lstInserts.Add(itemNew);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
if (lstInserts.Count() > 0)
|
||||
SQLiteHelper.Instance.InsertAll(lstInserts);
|
||||
|
||||
if (lstUpdates.Count() > 0)
|
||||
SQLiteHelper.Instance.UpdateAll(lstUpdates);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("ProfileExHandler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx)
|
||||
{
|
||||
profileEx = new()
|
||||
@@ -73,11 +107,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
//foreach (var item in _lstProfileEx)
|
||||
//{
|
||||
// SQLiteHelper.Instance.Replace(item);
|
||||
//}
|
||||
SQLiteHelper.Instance.UpdateAll(_lstProfileEx);
|
||||
SaveQueueIndexIds();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -29,15 +29,23 @@ namespace v2rayN.Handler
|
||||
/// <returns>true: one of connection is successfully updated proxy settings</returns>
|
||||
public static bool SetProxy(string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
// set proxy for LAN
|
||||
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
|
||||
// set proxy for dial up connections
|
||||
var connections = EnumerateRasEntries();
|
||||
foreach (var connection in connections)
|
||||
try
|
||||
{
|
||||
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
|
||||
// set proxy for LAN
|
||||
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
|
||||
// set proxy for dial up connections
|
||||
var connections = EnumerateRasEntries();
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
|
||||
|
||||
@@ -1,998 +0,0 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.Text.RegularExpressions;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
{
|
||||
internal class ShareHandler
|
||||
{
|
||||
#region GetShareUrl
|
||||
|
||||
/// <summary>
|
||||
/// GetShareUrl
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static string? GetShareUrl(ProfileItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
string? url = string.Empty;
|
||||
|
||||
url = item.configType switch
|
||||
{
|
||||
EConfigType.VMess => ShareVmess(item),
|
||||
EConfigType.Shadowsocks => ShareShadowsocks(item),
|
||||
EConfigType.Socks => ShareSocks(item),
|
||||
EConfigType.Trojan => ShareTrojan(item),
|
||||
EConfigType.VLESS => ShareVLESS(item),
|
||||
EConfigType.Hysteria2 => ShareHysteria2(item),
|
||||
EConfigType.Tuic => ShareTuic(item),
|
||||
EConfigType.Wireguard => ShareWireguard(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static string ShareVmess(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
|
||||
VmessQRCode vmessQRCode = new()
|
||||
{
|
||||
v = item.configVersion,
|
||||
ps = item.remarks.TrimEx(),
|
||||
add = item.address,
|
||||
port = item.port,
|
||||
id = item.id,
|
||||
aid = item.alterId,
|
||||
scy = item.security,
|
||||
net = item.network,
|
||||
type = item.headerType,
|
||||
host = item.requestHost,
|
||||
path = item.path,
|
||||
tls = item.streamSecurity,
|
||||
sni = item.sni,
|
||||
alpn = item.alpn,
|
||||
fp = item.fingerprint
|
||||
};
|
||||
|
||||
url = JsonUtils.Serialize(vmessQRCode);
|
||||
url = Utils.Base64Encode(url);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareShadowsocks(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new Sip002
|
||||
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareSocks(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new
|
||||
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareTrojan(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
GetStdTransport(item, null, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareVLESS(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.security);
|
||||
}
|
||||
else
|
||||
{
|
||||
dicQuery.Add("encryption", Global.None);
|
||||
}
|
||||
GetStdTransport(item, Global.None, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareHysteria2(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
|
||||
}
|
||||
dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0");
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareTuic(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.headerType);
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
$"{item.id}:{item.security}",
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string ShareWireguard(ProfileItem item)
|
||||
{
|
||||
string url = string.Empty;
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
Utils.UrlEncode(item.id),
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
private static string GetIpv6(string address)
|
||||
{
|
||||
return Utils.IsIpv6(address) ? $"[{address}]" : address;
|
||||
}
|
||||
|
||||
private static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(item.flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.flow);
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.streamSecurity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (securityDef != null)
|
||||
{
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.spiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
|
||||
}
|
||||
|
||||
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
|
||||
if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode)
|
||||
{
|
||||
dicQuery.Add("mode", Utils.UrlEncode(item.headerType));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion GetShareUrl
|
||||
|
||||
#region ImportShareUrl
|
||||
|
||||
/// <summary>
|
||||
/// 从剪贴板导入URL
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
public static ProfileItem? ImportFromClipboardConfig(string clipboardData, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? profileItem;
|
||||
|
||||
try
|
||||
{
|
||||
string result = clipboardData.TrimEx();
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
|
||||
{
|
||||
int indexSplit = result.IndexOf("?");
|
||||
if (indexSplit > 0)
|
||||
{
|
||||
profileItem = ResolveStdVmess(result) ?? ResolveVmess4Kitsunebi(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
profileItem = ResolveVmess(result, out msg);
|
||||
}
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
|
||||
{
|
||||
profileItem = ResolveSSLegacy(result) ?? ResolveSip002(result);
|
||||
if (profileItem == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (profileItem.address.Length == 0 || profileItem.port == 0 || profileItem.security.Length == 0 || profileItem.id.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
profileItem.configType = EConfigType.Shadowsocks;
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Socks]))
|
||||
{
|
||||
profileItem = ResolveSocksNew(result) ?? ResolveSocks(result);
|
||||
if (profileItem == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (profileItem.address.Length == 0 || profileItem.port == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
profileItem.configType = EConfigType.Socks;
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
|
||||
{
|
||||
profileItem = ResolveTrojan(result);
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
|
||||
{
|
||||
profileItem = ResolveStdVLESS(result);
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || result.StartsWith(Global.Hysteria2ProtocolShare))
|
||||
{
|
||||
profileItem = ResolveHysteria2(result);
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Tuic]))
|
||||
{
|
||||
profileItem = ResolveTuic(result);
|
||||
}
|
||||
else if (result.StartsWith(Global.ProtocolShares[EConfigType.Wireguard]))
|
||||
{
|
||||
profileItem = ResolveWireguard(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = ResUI.NonvmessOrssProtocol;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
msg = ResUI.Incorrectconfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveVmess(string result, out string msg)
|
||||
{
|
||||
msg = string.Empty;
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
configType = EConfigType.VMess
|
||||
};
|
||||
|
||||
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
|
||||
result = Utils.Base64Decode(result);
|
||||
|
||||
//转成Json
|
||||
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
|
||||
if (vmessQRCode == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
profileItem.network = Global.DefaultNetwork;
|
||||
profileItem.headerType = Global.None;
|
||||
|
||||
profileItem.configVersion = Utils.ToInt(vmessQRCode.v);
|
||||
profileItem.remarks = Utils.ToString(vmessQRCode.ps);
|
||||
profileItem.address = Utils.ToString(vmessQRCode.add);
|
||||
profileItem.port = Utils.ToInt(vmessQRCode.port);
|
||||
profileItem.id = Utils.ToString(vmessQRCode.id);
|
||||
profileItem.alterId = Utils.ToInt(vmessQRCode.aid);
|
||||
profileItem.security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
profileItem.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
|
||||
{
|
||||
profileItem.network = vmessQRCode.net;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
|
||||
{
|
||||
profileItem.headerType = vmessQRCode.type;
|
||||
}
|
||||
|
||||
profileItem.requestHost = Utils.ToString(vmessQRCode.host);
|
||||
profileItem.path = Utils.ToString(vmessQRCode.path);
|
||||
profileItem.streamSecurity = Utils.ToString(vmessQRCode.tls);
|
||||
profileItem.sni = Utils.ToString(vmessQRCode.sni);
|
||||
profileItem.alpn = Utils.ToString(vmessQRCode.alpn);
|
||||
profileItem.fingerprint = Utils.ToString(vmessQRCode.fp);
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveVmess4Kitsunebi(string result)
|
||||
{
|
||||
ProfileItem profileItem = new()
|
||||
{
|
||||
configType = EConfigType.VMess
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
|
||||
int indexSplit = result.IndexOf("?");
|
||||
if (indexSplit > 0)
|
||||
{
|
||||
result = result[..indexSplit];
|
||||
}
|
||||
result = Utils.Base64Decode(result);
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
string[] arr22 = arr1[1].Split(':');
|
||||
if (arr21.Length != 2 || arr22.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
profileItem.address = arr22[0];
|
||||
profileItem.port = Utils.ToInt(arr22[1]);
|
||||
profileItem.security = arr21[0];
|
||||
profileItem.id = arr21[1];
|
||||
|
||||
profileItem.network = Global.DefaultNetwork;
|
||||
profileItem.headerType = Global.None;
|
||||
profileItem.remarks = "Alien";
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveStdVmess(string result)
|
||||
{
|
||||
ProfileItem i = new()
|
||||
{
|
||||
configType = EConfigType.VMess,
|
||||
security = "auto"
|
||||
};
|
||||
|
||||
Uri u = new(result);
|
||||
|
||||
i.address = u.IdnHost;
|
||||
i.port = u.Port;
|
||||
i.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var query = Utils.ParseQueryString(u.Query);
|
||||
|
||||
var m = StdVmessUserInfo.Match(u.UserInfo);
|
||||
if (!m.Success) return null;
|
||||
|
||||
i.id = m.Groups["id"].Value;
|
||||
|
||||
if (m.Groups["streamSecurity"].Success)
|
||||
{
|
||||
i.streamSecurity = m.Groups["streamSecurity"].Value;
|
||||
}
|
||||
switch (i.streamSecurity)
|
||||
{
|
||||
case Global.StreamSecurity:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!Utils.IsNullOrEmpty(i.streamSecurity))
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
|
||||
i.network = m.Groups["network"].Value;
|
||||
switch (i.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
string t1 = query["type"] ?? Global.None;
|
||||
i.headerType = t1;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
i.headerType = query["type"] ?? Global.None;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
string p1 = query["path"] ?? "/";
|
||||
string h1 = query["host"] ?? "";
|
||||
i.requestHost = Utils.UrlDecode(h1);
|
||||
i.path = p1;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
i.network = nameof(ETransport.h2);
|
||||
string p2 = query["path"] ?? "/";
|
||||
string h2 = query["host"] ?? "";
|
||||
i.requestHost = Utils.UrlDecode(h2);
|
||||
i.path = p2;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
string s = query["security"] ?? Global.None;
|
||||
string k = query["key"] ?? "";
|
||||
string t3 = query["type"] ?? Global.None;
|
||||
i.headerType = t3;
|
||||
i.requestHost = Utils.UrlDecode(s);
|
||||
i.path = k;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSip002(string result)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem server = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped);
|
||||
//2022-blake3
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
server.security = userInfoParts[0];
|
||||
server.id = Utils.UrlDecode(userInfoParts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// parse base64 UserInfo
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
server.security = userInfoParts[0];
|
||||
server.id = userInfoParts[1];
|
||||
}
|
||||
|
||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||
if (queryParameters["plugin"] != null)
|
||||
{
|
||||
//obfs-host exists
|
||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
server.network = Global.DefaultNetwork;
|
||||
server.headerType = Global.TcpHeaderHttp;
|
||||
server.requestHost = obfsHost ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static ProfileItem? ResolveSSLegacy(string result)
|
||||
{
|
||||
var match = UrlFinder.Match(result);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
||||
ProfileItem server = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (!Utils.IsNullOrEmpty(tag))
|
||||
{
|
||||
server.remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
Match details;
|
||||
try
|
||||
{
|
||||
details = DetailsParser.Match(Utils.Base64Decode(base64));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!details.Success)
|
||||
return null;
|
||||
server.security = details.Groups["method"].Value;
|
||||
server.id = details.Groups["password"].Value;
|
||||
server.address = details.Groups["hostname"].Value;
|
||||
server.port = Utils.ToInt(details.Groups["port"].Value);
|
||||
return server;
|
||||
}
|
||||
|
||||
private static readonly Regex StdVmessUserInfo = new(
|
||||
@"^(?<network>[a-z]+)(\+(?<streamSecurity>[a-z]+))?:(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled);
|
||||
|
||||
private static ProfileItem? ResolveSocks(string result)
|
||||
{
|
||||
ProfileItem profileItem = new()
|
||||
{
|
||||
configType = EConfigType.Socks
|
||||
};
|
||||
result = result[Global.ProtocolShares[EConfigType.Socks].Length..];
|
||||
//remark
|
||||
int indexRemark = result.IndexOf("#");
|
||||
if (indexRemark > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
profileItem.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
|
||||
}
|
||||
catch { }
|
||||
result = result[..indexRemark];
|
||||
}
|
||||
//part decode
|
||||
int indexS = result.IndexOf("@");
|
||||
if (indexS > 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
//string[] arr22 = arr1[1].Split(':');
|
||||
int indexPort = arr1[1].LastIndexOf(":");
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
profileItem.address = arr1[1][..indexPort];
|
||||
profileItem.port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||
profileItem.security = arr21[0];
|
||||
profileItem.id = arr21[1];
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocksNew(string result)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem server = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
|
||||
// parse base64 UserInfo
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
server.security = userInfoParts[0];
|
||||
server.id = userInfoParts[1];
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private static ProfileItem ResolveTrojan(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Trojan
|
||||
};
|
||||
|
||||
Uri url = new(result);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem ResolveStdVLESS(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.VLESS,
|
||||
security = Global.None
|
||||
};
|
||||
|
||||
Uri url = new(result);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
item.security = query["encryption"] ?? Global.None;
|
||||
item.streamSecurity = query["security"] ?? "";
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem ResolveHysteria2(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Hysteria2
|
||||
};
|
||||
|
||||
Uri url = new(result);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.path = Utils.UrlDecode(query["obfs-password"] ?? "");
|
||||
item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem ResolveTuic(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Tuic
|
||||
};
|
||||
|
||||
Uri url = new(result);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.id = userInfoParts[0];
|
||||
item.security = userInfoParts[1];
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.headerType = query["congestion_control"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem ResolveWireguard(string result)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
configType = EConfigType.Wireguard
|
||||
};
|
||||
|
||||
Uri url = new(result);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
|
||||
item.publicKey = Utils.UrlDecode(query["publickey"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["reserved"] ?? "");
|
||||
item.requestHost = Utils.UrlDecode(query["address"] ?? "");
|
||||
item.shortId = Utils.UrlDecode(query["mtu"] ?? "");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
|
||||
{
|
||||
item.flow = query["flow"] ?? "";
|
||||
item.streamSecurity = query["security"] ?? "";
|
||||
item.sni = query["sni"] ?? "";
|
||||
item.alpn = Utils.UrlDecode(query["alpn"] ?? "");
|
||||
item.fingerprint = Utils.UrlDecode(query["fp"] ?? "");
|
||||
item.publicKey = Utils.UrlDecode(query["pbk"] ?? "");
|
||||
item.shortId = Utils.UrlDecode(query["sid"] ?? "");
|
||||
item.spiderX = Utils.UrlDecode(query["spx"] ?? "");
|
||||
|
||||
item.network = query["type"] ?? nameof(ETransport.tcp);
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.path = Utils.UrlDecode(query["seed"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
item.network = nameof(ETransport.h2);
|
||||
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
item.headerType = query["headerType"] ?? Global.None;
|
||||
item.requestHost = query["quicSecurity"] ?? Global.None;
|
||||
item.path = Utils.UrlDecode(query["key"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
item.requestHost = Utils.UrlDecode(query["authority"] ?? "");
|
||||
item.path = Utils.UrlDecode(query["serviceName"] ?? "");
|
||||
item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion ImportShareUrl
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
@@ -27,7 +28,7 @@ namespace v2rayN.Handler
|
||||
|
||||
try
|
||||
{
|
||||
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort}/traffic";
|
||||
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort2}/traffic";
|
||||
|
||||
if (webSocket == null)
|
||||
{
|
||||
@@ -61,6 +62,7 @@ namespace v2rayN.Handler
|
||||
|
||||
while (!_exitFlag)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
try
|
||||
{
|
||||
if (!(_config.runningCoreType is ECoreType.sing_box or ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo))
|
||||
@@ -105,10 +107,6 @@ namespace v2rayN.Handler
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using ProtosLib.Statistics;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
@@ -49,6 +50,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
while (!_exitFlag)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
try
|
||||
{
|
||||
if (!(_config.runningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5 or ECoreType.SagerNet))
|
||||
@@ -60,7 +62,8 @@ namespace v2rayN.Handler
|
||||
QueryStatsResponse? res = null;
|
||||
try
|
||||
{
|
||||
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
||||
if (_client != null)
|
||||
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -72,8 +75,8 @@ namespace v2rayN.Handler
|
||||
_updateFunc(server);
|
||||
}
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
if (_channel != null) await _channel.ConnectAsync();
|
||||
if (_channel != null)
|
||||
await _channel.ConnectAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -84,6 +87,8 @@ namespace v2rayN.Handler
|
||||
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
|
||||
{
|
||||
server = new();
|
||||
long aggregateProxyUp = 0;
|
||||
long aggregateProxyDown = 0;
|
||||
try
|
||||
{
|
||||
foreach (Stat stat in source)
|
||||
@@ -98,15 +103,15 @@ namespace v2rayN.Handler
|
||||
name = nStr[1];
|
||||
type = nStr[3];
|
||||
|
||||
if (name == Global.ProxyTag)
|
||||
if (name.StartsWith(Global.ProxyTag))
|
||||
{
|
||||
if (type == "uplink")
|
||||
{
|
||||
server.proxyUp = value;
|
||||
aggregateProxyUp += value;
|
||||
}
|
||||
else if (type == "downlink")
|
||||
{
|
||||
server.proxyDown = value;
|
||||
aggregateProxyDown += value;
|
||||
}
|
||||
}
|
||||
else if (name == Global.DirectTag)
|
||||
@@ -121,6 +126,8 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
}
|
||||
server.proxyUp = aggregateProxyUp;
|
||||
server.proxyDown = aggregateProxyDown;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1,34 +1,12 @@
|
||||
using PacLib;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
{
|
||||
public static class SysProxyHandle
|
||||
{
|
||||
//private const string _userWininetConfigFile = "user-wininet.json";
|
||||
|
||||
//private static string _queryStr;
|
||||
|
||||
// In general, this won't change
|
||||
// format:
|
||||
// <flags><CR-LF>
|
||||
// <proxy-server><CR-LF>
|
||||
// <bypass-list><CR-LF>
|
||||
// <pac-url>
|
||||
|
||||
private enum RET_ERRORS : int
|
||||
{
|
||||
RET_NO_ERROR = 0,
|
||||
INVALID_FORMAT = 1,
|
||||
NO_PERMISSION = 2,
|
||||
SYSCALL_FAILED = 3,
|
||||
NO_MEMORY = 4,
|
||||
INVAILD_OPTION_COUNT = 5,
|
||||
};
|
||||
|
||||
static SysProxyHandle()
|
||||
{
|
||||
}
|
||||
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
|
||||
public static bool UpdateSysProxy(Config config, bool forceDisable)
|
||||
{
|
||||
@@ -64,11 +42,17 @@ namespace v2rayN.Handler
|
||||
.Replace("{http_port}", port.ToString())
|
||||
.Replace("{socks_port}", portSocks.ToString());
|
||||
}
|
||||
ProxySetting.SetProxy(strProxy, strExceptions, 2); // set a named proxy
|
||||
if (!ProxySetting.SetProxy(strProxy, strExceptions, 2))
|
||||
{
|
||||
SetProxy(strProxy, strExceptions, 2);
|
||||
}
|
||||
}
|
||||
else if (type == ESysProxyType.ForcedClear)
|
||||
{
|
||||
ProxySetting.UnsetProxy(); // set to no proxy
|
||||
if (!ProxySetting.UnsetProxy())
|
||||
{
|
||||
UnsetProxy();
|
||||
}
|
||||
}
|
||||
else if (type == ESysProxyType.Unchanged)
|
||||
{
|
||||
@@ -77,7 +61,10 @@ namespace v2rayN.Handler
|
||||
{
|
||||
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
|
||||
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
||||
ProxySetting.SetProxy(strProxy, "", 4); // use pac script url for auto-config proxy
|
||||
if (!ProxySetting.SetProxy(strProxy, "", 4))
|
||||
{
|
||||
SetProxy(strProxy, "", 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (type != ESysProxyType.Pac)
|
||||
@@ -94,14 +81,38 @@ namespace v2rayN.Handler
|
||||
|
||||
public static void ResetIEProxy4WindowsShutDown()
|
||||
{
|
||||
try
|
||||
SetProxy(null, null, 1);
|
||||
}
|
||||
|
||||
private static void UnsetProxy()
|
||||
{
|
||||
SetProxy(null, null, 1);
|
||||
}
|
||||
|
||||
private static bool SetProxy(string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
if (type == 1)
|
||||
{
|
||||
//TODO To be verified
|
||||
Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0);
|
||||
Utils.RegWriteValue(_regPath, "ProxyEnable", 0);
|
||||
Utils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
catch
|
||||
if (type == 2)
|
||||
{
|
||||
Utils.RegWriteValue(_regPath, "ProxyEnable", 1);
|
||||
Utils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
else if (type == 4)
|
||||
{
|
||||
Utils.RegWriteValue(_regPath, "ProxyEnable", 0);
|
||||
Utils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
|
||||
Utils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
@@ -299,9 +300,6 @@ namespace v2rayN.Handler
|
||||
{
|
||||
await UpdateGeoFile("geosite", _config, update);
|
||||
await UpdateGeoFile("geoip", _config, update);
|
||||
|
||||
//await UpdateGeoFile4Singbox("geosite", _config, false, update);
|
||||
//await UpdateGeoFile4Singbox("geoip", _config, true, update);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -327,7 +325,7 @@ namespace v2rayN.Handler
|
||||
var result = await (new DownloadHandle()).DownloadStringAsync(url, true, "");
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
responseHandler(type, result, preRelease);
|
||||
ResponseHandler(type, result, preRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -345,7 +343,7 @@ namespace v2rayN.Handler
|
||||
/// <summary>
|
||||
/// 获取V2RayCore版本
|
||||
/// </summary>
|
||||
private SemanticVersion getCoreVersion(ECoreType type)
|
||||
private SemanticVersion GetCoreVersion(ECoreType type)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -410,7 +408,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
private void ResponseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -431,7 +429,7 @@ namespace v2rayN.Handler
|
||||
case ECoreType.Xray:
|
||||
case ECoreType.v2fly_v5:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
|
||||
string osBit = "64";
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
@@ -456,7 +454,7 @@ namespace v2rayN.Handler
|
||||
case ECoreType.clash_meta:
|
||||
case ECoreType.mihomo:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion);
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
@@ -477,7 +475,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
case ECoreType.sing_box:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
|
||||
17
v2rayN/v2rayN/Models/ClashConnectionModel.cs
Normal file
17
v2rayN/v2rayN/Models/ClashConnectionModel.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
public class ClashConnectionModel
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string network { get; set; }
|
||||
public string type { get; set; }
|
||||
public string host { get; set; }
|
||||
public ulong upload { get; set; }
|
||||
public ulong download { get; set; }
|
||||
public string uploadTraffic { get; set; }
|
||||
public string downloadTraffic { get; set; }
|
||||
public double time { get; set; }
|
||||
public string elapsed { get; set; }
|
||||
public string chain { get; set; }
|
||||
}
|
||||
}
|
||||
37
v2rayN/v2rayN/Models/ClashConnections.cs
Normal file
37
v2rayN/v2rayN/Models/ClashConnections.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
public class ClashConnections
|
||||
{
|
||||
public ulong downloadTotal { get; set; }
|
||||
public ulong uploadTotal { get; set; }
|
||||
public List<ConnectionItem>? connections { get; set; }
|
||||
}
|
||||
|
||||
public class ConnectionItem
|
||||
{
|
||||
public string id { get; set; } = string.Empty;
|
||||
public MetadataItem metadata { get; set; }
|
||||
public ulong upload { get; set; }
|
||||
public ulong download { get; set; }
|
||||
public DateTime start { get; set; }
|
||||
public List<string>? chains { get; set; }
|
||||
public string rule { get; set; }
|
||||
public string rulePayload { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataItem
|
||||
{
|
||||
public string network { get; set; }
|
||||
public string type { get; set; }
|
||||
public string sourceIP { get; set; }
|
||||
public string destinationIP { get; set; }
|
||||
public string sourcePort { get; set; }
|
||||
public string destinationPort { get; set; }
|
||||
public string host { get; set; }
|
||||
public string nsMode { get; set; }
|
||||
public object uid { get; set; }
|
||||
public string process { get; set; }
|
||||
public string processPath { get; set; }
|
||||
public string remoteDestination { get; set; }
|
||||
}
|
||||
}
|
||||
19
v2rayN/v2rayN/Models/ClashProviders.cs
Normal file
19
v2rayN/v2rayN/Models/ClashProviders.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
using static v2rayN.Models.ClashProxies;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
public class ClashProviders
|
||||
{
|
||||
public Dictionary<String, ProvidersItem> providers { get; set; }
|
||||
|
||||
public class ProvidersItem
|
||||
{
|
||||
public string name { get; set; }
|
||||
public ProxiesItem[] proxies { get; set; }
|
||||
public string type { get; set; }
|
||||
public string vehicleType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
24
v2rayN/v2rayN/Models/ClashProxies.cs
Normal file
24
v2rayN/v2rayN/Models/ClashProxies.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
public class ClashProxies
|
||||
{
|
||||
public Dictionary<String, ProxiesItem> proxies { get; set; }
|
||||
|
||||
public class ProxiesItem
|
||||
{
|
||||
public string[] all { get; set; }
|
||||
public List<HistoryItem> history { get; set; }
|
||||
public string name { get; set; }
|
||||
public string type { get; set; }
|
||||
public bool udp { get; set; }
|
||||
public string now { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
|
||||
public class HistoryItem
|
||||
{
|
||||
public string time { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
26
v2rayN/v2rayN/Models/ClashProxyModel.cs
Normal file
26
v2rayN/v2rayN/Models/ClashProxyModel.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ClashProxyModel
|
||||
{
|
||||
[Reactive]
|
||||
public string name { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string type { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string now { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int delay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string delayName { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool isActive { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace v2rayN.Models
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本软件配置文件实体类
|
||||
@@ -31,6 +33,7 @@
|
||||
public SpeedTestItem speedTestItem { get; set; }
|
||||
public Mux4SboxItem mux4SboxItem { get; set; }
|
||||
public HysteriaItem hysteriaItem { get; set; }
|
||||
public ClashUIItem clashUIItem { get; set; }
|
||||
public List<InItem> inbound { get; set; }
|
||||
public List<KeyEventItem> globalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> coreTypeItem { get; set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Windows.Input;
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
@@ -31,6 +32,10 @@ namespace v2rayN.Models
|
||||
/// 默认用户代理
|
||||
/// </summary>
|
||||
public string defUserAgent { get; set; }
|
||||
|
||||
public bool enableFragment { get; set; }
|
||||
|
||||
public bool enableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -43,8 +48,8 @@ namespace v2rayN.Models
|
||||
public bool udpEnabled { get; set; }
|
||||
|
||||
public bool sniffingEnabled { get; set; } = true;
|
||||
public List<string>? destOverride { get; set; } = ["http", "tls"];
|
||||
public bool routeOnly { get; set; }
|
||||
|
||||
public bool allowLANConn { get; set; }
|
||||
|
||||
public bool newPort4LAN { get; set; }
|
||||
@@ -122,7 +127,7 @@ namespace v2rayN.Models
|
||||
public int currentFontSize { get; set; }
|
||||
public bool enableDragDropSort { get; set; }
|
||||
public bool doubleClick2Activate { get; set; }
|
||||
public bool autoHideStartup { get; set; } = true;
|
||||
public bool autoHideStartup { get; set; }
|
||||
public string mainMsgFilter { get; set; }
|
||||
public List<ColumnItem> mainColumnItem { get; set; }
|
||||
}
|
||||
@@ -178,13 +183,8 @@ namespace v2rayN.Models
|
||||
[Serializable]
|
||||
public class RoutingBasicItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 域名解析策略
|
||||
/// </summary>
|
||||
public string domainStrategy { get; set; }
|
||||
|
||||
public string domainStrategy4Singbox { get; set; }
|
||||
|
||||
public string domainMatcher { get; set; }
|
||||
public string routingIndexId { get; set; }
|
||||
public bool enableRoutingAdvanced { get; set; }
|
||||
@@ -211,4 +211,19 @@ namespace v2rayN.Models
|
||||
public int up_mbps { get; set; }
|
||||
public int down_mbps { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ClashUIItem
|
||||
{
|
||||
public ERuleMode ruleMode { get; set; }
|
||||
public bool showInTaskbar { get; set; }
|
||||
public bool enableIPv6 { get; set; }
|
||||
public bool enableMixinContent { get; set; }
|
||||
public int proxiesSorting { get; set; }
|
||||
public bool proxiesAutoRefresh { get; set; }
|
||||
public int proxiesAutoDelayTestInterval { get; set; } = 10;
|
||||
public int connectionsSorting { get; set; }
|
||||
public bool connectionsAutoRefresh { get; set; }
|
||||
public int connectionsRefreshInterval { get; set; } = 2;
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ConfigOld
|
||||
{
|
||||
#region property
|
||||
|
||||
/// <summary>
|
||||
/// 允许日志
|
||||
/// </summary>
|
||||
public bool logEnabled
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public string loglevel
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string indexId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 允许Mux多路复用
|
||||
/// </summary>
|
||||
public bool muxEnabled
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public ESysProxyType sysProxyType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用实时网速和流量统计
|
||||
/// </summary>
|
||||
public bool enableStatistics
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 去重时优先保留较旧(顶部)节点
|
||||
/// </summary>
|
||||
public bool keepOlderDedupl
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视图刷新率
|
||||
/// </summary>
|
||||
public int statisticsFreshRate
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义远程DNS
|
||||
/// </summary>
|
||||
public string remoteDNS
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outbound Freedom domainStrategy
|
||||
/// </summary>
|
||||
public string domainStrategy4Freedom
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许不安全连接
|
||||
/// </summary>
|
||||
public bool defAllowInsecure
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 域名解析策略
|
||||
/// </summary>
|
||||
public string domainStrategy
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string domainMatcher
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int routingIndex
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool enableRoutingAdvanced
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool ignoreGeoUpdateCore
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// systemProxyExceptions
|
||||
/// </summary>
|
||||
public string systemProxyExceptions
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string systemProxyAdvancedProtocol { get; set; }
|
||||
|
||||
public int autoUpdateInterval { get; set; } = 0;
|
||||
|
||||
public int autoUpdateSubInterval { get; set; } = 0;
|
||||
|
||||
public bool checkPreReleaseUpdate { get; set; } = false;
|
||||
|
||||
public bool enableSecurityProtocolTls13
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int trayMenuServersLimit { get; set; }
|
||||
|
||||
#endregion property
|
||||
|
||||
#region other entities
|
||||
|
||||
/// <summary>
|
||||
/// 本地监听
|
||||
/// </summary>
|
||||
public List<InItem> inbound
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// vmess服务器信息
|
||||
/// </summary>
|
||||
public List<VmessItem> vmess
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KcpItem
|
||||
/// </summary>
|
||||
public KcpItem kcpItem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅
|
||||
/// </summary>
|
||||
public List<SubItem> subItem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI
|
||||
/// </summary>
|
||||
public UIItem uiItem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<RoutingItemOld> routings
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ConstItem constItem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<KeyEventItem> globalHotkeys
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<CoreTypeItem> coreTypeItem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
#endregion other entities
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class VmessItem
|
||||
{
|
||||
public VmessItem()
|
||||
{
|
||||
indexId = string.Empty;
|
||||
configType = EConfigType.VMess;
|
||||
configVersion = 2;
|
||||
sort = 0;
|
||||
address = string.Empty;
|
||||
port = 0;
|
||||
id = string.Empty;
|
||||
alterId = 0;
|
||||
security = string.Empty;
|
||||
network = string.Empty;
|
||||
remarks = string.Empty;
|
||||
headerType = string.Empty;
|
||||
requestHost = string.Empty;
|
||||
path = string.Empty;
|
||||
streamSecurity = string.Empty;
|
||||
allowInsecure = string.Empty;
|
||||
testResult = string.Empty;
|
||||
subid = string.Empty;
|
||||
flow = string.Empty;
|
||||
groupId = string.Empty;
|
||||
}
|
||||
|
||||
public string indexId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// config type(1=normal,2=custom)
|
||||
/// </summary>
|
||||
public EConfigType configType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 版本(现在=2)
|
||||
/// </summary>
|
||||
public int configVersion
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int sort
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务器地址
|
||||
/// </summary>
|
||||
public string address
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务器端口
|
||||
/// </summary>
|
||||
public int port
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务器ID
|
||||
/// </summary>
|
||||
public string id
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务器额外ID
|
||||
/// </summary>
|
||||
public int alterId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地安全策略
|
||||
/// </summary>
|
||||
public string security
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// tcp,kcp,ws,h2,quic
|
||||
/// </summary>
|
||||
public string network
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string remarks
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 伪装类型
|
||||
/// </summary>
|
||||
public string headerType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 伪装的域名
|
||||
/// </summary>
|
||||
public string requestHost
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ws h2 path
|
||||
/// </summary>
|
||||
public string path
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 传输层安全
|
||||
/// </summary>
|
||||
public string streamSecurity
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许不安全连接(用于客户端)
|
||||
/// </summary>
|
||||
public string allowInsecure
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string testResult
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SubItem id
|
||||
/// </summary>
|
||||
public string subid
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VLESS flow
|
||||
/// </summary>
|
||||
public string flow
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// tls sni
|
||||
/// </summary>
|
||||
public string sni
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string groupId
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
|
||||
public ECoreType? coreType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int preSocksPort
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string fingerprint { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RoutingItemOld
|
||||
{
|
||||
public string remarks
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string url
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<RulesItem> rules
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool enabled { get; set; } = true;
|
||||
|
||||
public bool locked
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string customIcon
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace v2rayN.Models
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreInfo
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SQLite;
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SQLite;
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace v2rayN.Models
|
||||
public bool enabled { get; set; } = true;
|
||||
public bool locked { get; set; }
|
||||
public string customIcon { get; set; }
|
||||
public string customRulesetPath4Singbox { get; set; }
|
||||
public string domainStrategy { get; set; }
|
||||
public string domainStrategy4Singbox { get; set; }
|
||||
public int sort { get; set; }
|
||||
|
||||
@@ -4,21 +4,22 @@
|
||||
public class RulesItem
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
public string? type { get; set; }
|
||||
|
||||
public string port { get; set; }
|
||||
public string? port { get; set; }
|
||||
public string? network { get; set; }
|
||||
|
||||
public List<string> inboundTag { get; set; }
|
||||
public List<string>? inboundTag { get; set; }
|
||||
|
||||
public string outboundTag { get; set; }
|
||||
public string? outboundTag { get; set; }
|
||||
|
||||
public List<string> ip { get; set; }
|
||||
public List<string>? ip { get; set; }
|
||||
|
||||
public List<string> domain { get; set; }
|
||||
public List<string>? domain { get; set; }
|
||||
|
||||
public List<string> protocol { get; set; }
|
||||
public List<string>? protocol { get; set; }
|
||||
|
||||
public List<string> process { get; set; }
|
||||
public List<string>? process { get; set; }
|
||||
|
||||
public bool enabled { get; set; } = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace v2rayN.Models
|
||||
using v2rayN.Enums;
|
||||
|
||||
namespace v2rayN.Models
|
||||
{
|
||||
[Serializable]
|
||||
internal class ServerTestItem
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
public List<Inbound4Sbox> inbounds { get; set; }
|
||||
public List<Outbound4Sbox> outbounds { get; set; }
|
||||
public Route4Sbox route { get; set; }
|
||||
public Experimental4Sbox experimental { get; set; }
|
||||
public Experimental4Sbox? experimental { get; set; }
|
||||
}
|
||||
|
||||
public class Log4Sbox
|
||||
@@ -28,6 +28,7 @@
|
||||
public bool? disable_expire { get; set; }
|
||||
public bool? independent_cache { get; set; }
|
||||
public bool? reverse_mapping { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public Fakeip4Sbox? fakeip { get; set; }
|
||||
}
|
||||
|
||||
@@ -35,19 +36,23 @@
|
||||
{
|
||||
public bool? auto_detect_interface { get; set; }
|
||||
public List<Rule4Sbox> rules { get; set; }
|
||||
public List<Ruleset4Sbox>? rule_set { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Rule4Sbox
|
||||
{
|
||||
public string outbound { get; set; }
|
||||
public string server { get; set; }
|
||||
public string? outbound { get; set; }
|
||||
public string? server { get; set; }
|
||||
public bool? disable_cache { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? mode { get; set; }
|
||||
public bool? ip_is_private { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public bool? invert { get; set; }
|
||||
public List<string>? inbound { get; set; }
|
||||
public List<string>? protocol { get; set; }
|
||||
public string type { get; set; }
|
||||
public string mode { get; set; }
|
||||
public string network { get; set; }
|
||||
public List<string>? network { get; set; }
|
||||
public List<int>? port { get; set; }
|
||||
public List<string>? port_range { get; set; }
|
||||
public List<string>? geosite { get; set; }
|
||||
@@ -58,8 +63,9 @@
|
||||
public List<string>? geoip { get; set; }
|
||||
public List<string>? ip_cidr { get; set; }
|
||||
public List<string>? source_ip_cidr { get; set; }
|
||||
|
||||
public List<string>? process_name { get; set; }
|
||||
public List<string>? rule_set { get; set; }
|
||||
public List<Rule4Sbox>? rules { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -93,7 +99,7 @@
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string tag { get; set; }
|
||||
public string server { get; set; }
|
||||
public string? server { get; set; }
|
||||
public int? server_port { get; set; }
|
||||
public string uuid { get; set; }
|
||||
public string security { get; set; }
|
||||
@@ -182,11 +188,13 @@
|
||||
|
||||
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? tag { get; set; }
|
||||
public string? address { get; set; }
|
||||
public string? address_resolver { get; set; }
|
||||
public string? address_strategy { get; set; }
|
||||
public string? strategy { get; set; }
|
||||
public string? detour { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
}
|
||||
|
||||
public class Experimental4Sbox
|
||||
@@ -230,4 +238,15 @@
|
||||
public string? cache_id { get; set; }
|
||||
public bool? store_fakeip { get; set; }
|
||||
}
|
||||
|
||||
public class Ruleset4Sbox
|
||||
{
|
||||
public string? tag { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? format { get; set; }
|
||||
public string? path { get; set; }
|
||||
public string? url { get; set; }
|
||||
public string? download_detour { get; set; }
|
||||
public string? update_interval { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@ namespace v2rayN.Models
|
||||
public class Sniffing4Ray
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
public List<string> destOverride { get; set; }
|
||||
public List<string>? destOverride { get; set; }
|
||||
public bool routeOnly { get; set; }
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<VnextItem4Ray> vnext { get; set; }
|
||||
public List<VnextItem4Ray>? vnext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -253,6 +253,8 @@ namespace v2rayN.Models
|
||||
///
|
||||
/// </summary>
|
||||
public int? userLevel { get; set; }
|
||||
|
||||
public FragmentItem4Ray? fragment { get; set; }
|
||||
}
|
||||
|
||||
public class VnextItem4Ray
|
||||
@@ -288,17 +290,17 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string method { get; set; }
|
||||
public string? method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ota { get; set; }
|
||||
public bool? ota { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string password { get; set; }
|
||||
public string? password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -308,7 +310,7 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int level { get; set; }
|
||||
public int? level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// trojan
|
||||
@@ -336,7 +338,7 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int level { get; set; }
|
||||
public int? level { get; set; }
|
||||
}
|
||||
|
||||
public class Mux4Ray
|
||||
@@ -368,6 +370,12 @@ namespace v2rayN.Models
|
||||
public List<string> servers { get; set; }
|
||||
}
|
||||
|
||||
public class DnsServer4Ray
|
||||
{
|
||||
public string? address { get; set; }
|
||||
public List<string>? domains { get; set; }
|
||||
}
|
||||
|
||||
public class Routing4Ray
|
||||
{
|
||||
/// <summary>
|
||||
@@ -378,7 +386,7 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string domainMatcher { get; set; }
|
||||
public string? domainMatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -389,19 +397,20 @@ namespace v2rayN.Models
|
||||
[Serializable]
|
||||
public class RulesItem4Ray
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string? type { get; set; }
|
||||
|
||||
public string port { get; set; }
|
||||
public string? port { get; set; }
|
||||
public string? network { get; set; }
|
||||
|
||||
public List<string> inboundTag { get; set; }
|
||||
public List<string>? inboundTag { get; set; }
|
||||
|
||||
public string outboundTag { get; set; }
|
||||
public string? outboundTag { get; set; }
|
||||
|
||||
public List<string> ip { get; set; }
|
||||
public List<string>? ip { get; set; }
|
||||
|
||||
public List<string> domain { get; set; }
|
||||
public List<string>? domain { get; set; }
|
||||
|
||||
public List<string> protocol { get; set; }
|
||||
public List<string>? protocol { get; set; }
|
||||
}
|
||||
|
||||
public class StreamSettings4Ray
|
||||
@@ -419,47 +428,52 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public TlsSettings4Ray tlsSettings { get; set; }
|
||||
public TlsSettings4Ray? tlsSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tcp传输额外设置
|
||||
/// </summary>
|
||||
public TcpSettings4Ray tcpSettings { get; set; }
|
||||
public TcpSettings4Ray? tcpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Kcp传输额外设置
|
||||
/// </summary>
|
||||
public KcpSettings4Ray kcpSettings { get; set; }
|
||||
public KcpSettings4Ray? kcpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ws传输额外设置
|
||||
/// </summary>
|
||||
public WsSettings4Ray wsSettings { get; set; }
|
||||
public WsSettings4Ray? wsSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public SplithttpSettings4Ray? splithttpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// h2传输额外设置
|
||||
/// </summary>
|
||||
public HttpSettings4Ray httpSettings { get; set; }
|
||||
public HttpSettings4Ray? httpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// QUIC
|
||||
/// </summary>
|
||||
public QuicSettings4Ray quicSettings { get; set; }
|
||||
public QuicSettings4Ray? quicSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// VLESS only
|
||||
/// </summary>
|
||||
public TlsSettings4Ray realitySettings { get; set; }
|
||||
public TlsSettings4Ray? realitySettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// grpc
|
||||
/// </summary>
|
||||
public GrpcSettings4Ray grpcSettings { get; set; }
|
||||
public GrpcSettings4Ray? grpcSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// sockopt
|
||||
@@ -486,7 +500,7 @@ namespace v2rayN.Models
|
||||
|
||||
public string? fingerprint { get; set; }
|
||||
|
||||
public bool? show { get; set; } = false;
|
||||
public bool? show { get; set; }
|
||||
public string? publicKey { get; set; }
|
||||
public string? shortId { get; set; }
|
||||
public string? spiderX { get; set; }
|
||||
@@ -606,6 +620,17 @@ namespace v2rayN.Models
|
||||
public string? host { get; set; }
|
||||
}
|
||||
|
||||
public class SplithttpSettings4Ray
|
||||
{
|
||||
public string? path { get; set; }
|
||||
|
||||
public string? host { get; set; }
|
||||
|
||||
public int? maxUploadSize { get; set; }
|
||||
|
||||
public int? maxConcurrentUploads { get; set; }
|
||||
}
|
||||
|
||||
public class HttpSettings4Ray
|
||||
{
|
||||
/// <summary>
|
||||
@@ -665,4 +690,11 @@ namespace v2rayN.Models
|
||||
{
|
||||
public string? dialerProxy { get; set; }
|
||||
}
|
||||
|
||||
public class FragmentItem4Ray
|
||||
{
|
||||
public string? packets { get; set; }
|
||||
public string? length { get; set; }
|
||||
public string? interval { get; set; }
|
||||
}
|
||||
}
|
||||
305
v2rayN/v2rayN/Resx/ResUI.Designer.cs
generated
305
v2rayN/v2rayN/Resx/ResUI.Designer.cs
generated
@@ -411,6 +411,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Custom the rule-set of sing-box 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvCustomRulesetPath4Singbox {
|
||||
get {
|
||||
return ResourceManager.GetString("LvCustomRulesetPath4Singbox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enabled Update 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -618,6 +627,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Http] server 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddHttpServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddHttpServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Hysteria2] server 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -735,6 +753,24 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Close Connection 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuConnectionClose {
|
||||
get {
|
||||
return ResourceManager.GetString("menuConnectionClose", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Close All Connection 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuConnectionCloseAll {
|
||||
get {
|
||||
return ResourceManager.GetString("menuConnectionCloseAll", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Clone selected server 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -861,6 +897,42 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Direct 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuModeDirect {
|
||||
get {
|
||||
return ResourceManager.GetString("menuModeDirect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Global 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuModeGlobal {
|
||||
get {
|
||||
return ResourceManager.GetString("menuModeGlobal", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Do not change 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuModeNothing {
|
||||
get {
|
||||
return ResourceManager.GetString("menuModeNothing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Rule 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuModeRule {
|
||||
get {
|
||||
return ResourceManager.GetString("menuModeRule", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Move to bottom (B) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -960,6 +1032,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Open the storage location 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuOpenTheFileLocation {
|
||||
get {
|
||||
return ResourceManager.GetString("menuOpenTheFileLocation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Option Setting 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -978,6 +1059,42 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 All Node Latency Test 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuProxiesDelaytest {
|
||||
get {
|
||||
return ResourceManager.GetString("menuProxiesDelaytest", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Part Node Latency Test 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuProxiesDelaytestPart {
|
||||
get {
|
||||
return ResourceManager.GetString("menuProxiesDelaytestPart", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Refresh Proxies (F5) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuProxiesReload {
|
||||
get {
|
||||
return ResourceManager.GetString("menuProxiesReload", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Select active node (Enter) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuProxiesSelectActivity {
|
||||
get {
|
||||
return ResourceManager.GetString("menuProxiesSelectActivity", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Test servers real delay (Ctrl+R) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1149,6 +1266,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Rule mode 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRulemode {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRulemode", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remove Rule (Delete) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1563,6 +1689,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Successful operation. Click the settings menu to reboot the app. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string NeedRebootTips {
|
||||
get {
|
||||
return ResourceManager.GetString("NeedRebootTips", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Non-VMess or ss protocol 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1960,6 +2095,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Connections 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbConnections {
|
||||
get {
|
||||
return ResourceManager.GetString("TbConnections", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Core Type 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2230,6 +2374,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Proxies 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbProxies {
|
||||
get {
|
||||
return ResourceManager.GetString("TbProxies", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 PublicKey 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2599,6 +2752,15 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable cache file for sing-box (ruleset files) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsEnableCacheFile4Sbox {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsEnableCacheFile4Sbox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Check for pre-release updates 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2626,6 +2788,24 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable fragment 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsEnableFragment {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsEnableFragment", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Use Xray and enable non-Tun mode, which conflicts with the group previous proxy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsEnableFragmentTips {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsEnableFragmentTips", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable hardware acceleration(Require restart) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2852,7 +3032,7 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5; 的本地化字符串。
|
||||
/// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSocksPortTip {
|
||||
get {
|
||||
@@ -3076,6 +3256,123 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Sorting 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSorting {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSorting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Chain 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingChain {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingChain", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Default 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingDefault {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingDefault", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Delay 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingDelay {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingDelay", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Download Speed 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingDownSpeed {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingDownSpeed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Download Traffic 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingDownTraffic {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingDownTraffic", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Host 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingHost {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Name 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingName {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Network 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingNetwork {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingNetwork", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Time 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingTime {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingTime", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Type 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingType {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upload Speed 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingUpSpeed {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingUpSpeed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upload Traffic 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSortingUpTraffic {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSortingUpTraffic", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 SpiderX 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3149,7 +3446,7 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 * After setting this value, an socks service will be started using sing-box to provide functions such as speed display 的本地化字符串。
|
||||
/// 查找类似 * After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TipPreSocksPort {
|
||||
get {
|
||||
@@ -3203,7 +3500,7 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 *ws/httpupgrade path 的本地化字符串。
|
||||
/// 查找类似 *ws/httpupgrade/splithttp path 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TransportPathTip1 {
|
||||
get {
|
||||
@@ -3257,7 +3554,7 @@ namespace v2rayN.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 *ws/httpupgrade host 的本地化字符串。
|
||||
/// 查找类似 *ws/httpupgrade/splithttp host 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TransportRequestHostTip2 {
|
||||
get {
|
||||
|
||||
@@ -698,7 +698,7 @@
|
||||
<value>txtPreSocksPort</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* After setting this value, an socks service will be started using sing-box to provide functions such as speed display</value>
|
||||
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
@@ -1003,4 +1003,73 @@
|
||||
<data name="TbSettingsEnableHWA" xml:space="preserve">
|
||||
<value>فعالسازی شتابدهنده سختافزاری (نیاز به راهاندازی مجدد)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>مرتب سازی</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>Default</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>تاخیر</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>سرعت دانلود</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>ترافیک دانلود</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>نام</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>زمان</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>سرعت اپلود</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>ترافیک آپلود</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>اتصالات</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>بستن اتصال</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>تمام اتصالات را ببندید</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>پروکسی</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>نوع قانون</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>Direct</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>Global</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>تغییر نده</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>قانون</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>All Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>Part Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>Refresh Proxies (F5)</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>Select active node (Enter)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -359,7 +359,7 @@
|
||||
<value>Please fill in the correct custom DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -374,7 +374,7 @@
|
||||
<value>*http host Separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host Separated by commas (,)</value>
|
||||
@@ -701,7 +701,7 @@
|
||||
<value>txtPreSocksPort</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* After setting this value, an socks service will be started using sing-box to provide functions such as speed display</value>
|
||||
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
@@ -1037,7 +1037,7 @@
|
||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5;</value>
|
||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||
@@ -1198,4 +1198,103 @@
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>Add [Http] server</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Enable fragment</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Enable cache file for sing-box (ruleset files)</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>Custom the rule-set of sing-box</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>Successful operation. Click the settings menu to reboot the app.</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>Open the storage location</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>Sorting</value>
|
||||
</data>
|
||||
<data name="TbSortingChain" xml:space="preserve">
|
||||
<value>Chain</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>Default</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>Delay</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>Download Speed</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>Download Traffic</value>
|
||||
</data>
|
||||
<data name="TbSortingHost" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="TbSortingNetwork" xml:space="preserve">
|
||||
<value>Network</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>Time</value>
|
||||
</data>
|
||||
<data name="TbSortingType" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>Upload Speed</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>Upload Traffic</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>Connections</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>Close Connection</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>Close All Connection</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>Proxies</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>Rule mode</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>Direct</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>Global</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>Do not change</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>Rule</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>All Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>Part Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>Refresh Proxies (F5)</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>Select active node (Enter)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -701,7 +701,7 @@
|
||||
<value>txtPreSocksPort</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* После установки этого значения служба socks будет запущена с использованием sing-box для обеспечения таких функций, как отображение скорости</value>
|
||||
<value>* После установки этого значения служба socks будет запущена с использованием Xray/sing-box(Tun) для обеспечения таких функций, как отображение скорости</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Просмотр</value>
|
||||
@@ -1042,9 +1042,6 @@
|
||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP port=socks port+1</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Установите это с правами администратора</value>
|
||||
</data>
|
||||
|
||||
@@ -359,7 +359,7 @@
|
||||
<value>请填写正确的自定义DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -374,7 +374,7 @@
|
||||
<value>*http host中间逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host中间逗号(,)分隔</value>
|
||||
@@ -701,7 +701,7 @@
|
||||
<value>Socks端口</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* 自定义配置的Socks端口值,可不设置;当设置此值后,将使用sing-box额外启动一个前置Socks服务,提供分流和速度显示等功能</value>
|
||||
<value>* 自定义配置的Socks端口值,可不设置;当设置此值后,将使用Xray/sing-box(Tun)额外启动一个前置Socks服务,提供分流和速度显示等功能</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>浏览</value>
|
||||
@@ -1037,7 +1037,7 @@
|
||||
<value>拷贝字体TTF/TTC文件到目录guiFonts,重启设置</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http端口=socks端口+1;Pac端口=socks端口+4;API端口=socks端口+5;</value>
|
||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理员权限设置此项,在启动后获得管理员权限</value>
|
||||
@@ -1195,4 +1195,103 @@
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>添加[Http]服务器</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>启用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用Xray且非Tun模式启用,和分组前置代理冲突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>启用sing-box(规则集文件)的缓存文件</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>自定义sing-box rule-set</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>操作成功。请点击设置菜单重启应用。</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>打开存储所在的位置</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>排序</value>
|
||||
</data>
|
||||
<data name="TbSortingChain" xml:space="preserve">
|
||||
<value>路由链</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>默认</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>延迟</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>下载速度</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>下载流量</value>
|
||||
</data>
|
||||
<data name="TbSortingHost" xml:space="preserve">
|
||||
<value>主机</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>名称</value>
|
||||
</data>
|
||||
<data name="TbSortingNetwork" xml:space="preserve">
|
||||
<value>网络</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>时间</value>
|
||||
</data>
|
||||
<data name="TbSortingType" xml:space="preserve">
|
||||
<value>类型</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>上传速度</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>上传流量</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>当前连接</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>关闭连接</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>关闭所有连接</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>规则模式</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>直连</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>全局</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>随原配置</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>规则</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>全部节点延迟测试</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>当前部分节点延迟测试</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>刷新 (F5)</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>设为活动节点 (Enter)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -358,7 +358,7 @@
|
||||
<value>請填寫正確的自訂DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -373,7 +373,7 @@
|
||||
<value>*http host中間逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host中間逗號(,)分隔</value>
|
||||
@@ -700,7 +700,7 @@
|
||||
<value>SOCKS埠</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* 自訂配置的Socks埠值,可不設定;當設定此值後,將使用sing-box額外啟動一個前置Socks服務,提供分流和速度顯示等功能</value>
|
||||
<value>* 自訂配置的Socks埠值,可不設定;當設定此值後,將使用Xray/sing-box(Tun)額外啟動一個前置Socks服務,提供分流和速度顯示等功能</value>
|
||||
</data>
|
||||
<!--********************************************-->
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
@@ -1037,7 +1037,7 @@
|
||||
<value>複製字型TTF/TTC文件到目錄guiFonts,重啟設定</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP埠=SOCKS埠+1</value>
|
||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
|
||||
@@ -1168,4 +1168,25 @@
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>新增[Http]伺服器</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>啟用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用Xray且非Tun模式啟用,和分組前置代理衝突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>啟用sing-box(規則集文件)的緩存文件</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>自訂sing-box rule-set</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>操作成功。 請點選設定選單重啟應用程式。</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>打開儲存所在的位置</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -4,56 +4,7 @@
|
||||
"error": "Verror.log",
|
||||
"loglevel": "warning"
|
||||
},
|
||||
"inbounds": [{
|
||||
"tag": "tag1",
|
||||
"port": 10808,
|
||||
"protocol": "socks",
|
||||
"listen": "127.0.0.1",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"udp": true
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "tag2",
|
||||
"port": 10809,
|
||||
"protocol": "http",
|
||||
"listen": "127.0.0.1",
|
||||
"settings": {
|
||||
"allowTransparent": false
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "tag3",
|
||||
"port": 10809,
|
||||
"protocol": "http",
|
||||
"listen": "127.0.0.1",
|
||||
"settings": {
|
||||
"allowTransparent": false
|
||||
},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"inbounds": [],
|
||||
"outbounds": [{
|
||||
"tag": "proxy",
|
||||
"protocol": "vmess",
|
||||
|
||||
@@ -3,14 +3,7 @@
|
||||
"level": "debug",
|
||||
"timestamp": true
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "socks",
|
||||
"listen": "127.0.0.1",
|
||||
"listen_port": 10000
|
||||
}
|
||||
],
|
||||
"inbounds": [],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
|
||||
39
v2rayN/v2rayN/Sample/clash_mixin_yaml
Normal file
39
v2rayN/v2rayN/Sample/clash_mixin_yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# 配置文件内容不会被修改,混合行为只会发生在内存中
|
||||
#
|
||||
# 注意下面缩进,请用支持yaml显示的编辑器打开
|
||||
#
|
||||
# 使用clash配置文件关键字则覆盖原配置
|
||||
#
|
||||
# removed-rules 循环匹配rules数组每行,符合则移除当前行 (此规则请放最前面)
|
||||
#
|
||||
# append-rules 数组合并至原配置rules数组后
|
||||
# prepend-rules 数组合并至原配置rules数组前
|
||||
# append-proxies 数组合并至原配置proxies数组后
|
||||
# prepend-proxies 数组合并至原配置proxies数组前
|
||||
# append-proxy-groups 数组合并至原配置proxy-groups数组后
|
||||
# prepend-proxy-groups 数组合并至原配置proxy-groups数组前
|
||||
# append-rule-providers 数组合并至原配置rule-providers数组后
|
||||
# prepend-rule-providers 数组合并至原配置rule-providers数组前
|
||||
#
|
||||
|
||||
dns:
|
||||
enable: true
|
||||
enhanced-mode: fake-ip
|
||||
nameserver:
|
||||
- 114.114.114.114
|
||||
- 223.5.5.5
|
||||
- 8.8.8.8
|
||||
fallback: []
|
||||
fake-ip-filter:
|
||||
- +.stun.*.*
|
||||
- +.stun.*.*.*
|
||||
- +.stun.*.*.*.*
|
||||
- +.stun.*.*.*.*.*
|
||||
- "*.n.n.srv.nintendo.net"
|
||||
- +.stun.playstation.net
|
||||
- xbox.*.*.microsoft.com
|
||||
- "*.*.xboxlive.com"
|
||||
- "*.msftncsi.com"
|
||||
- "*.msftconnecttest.com"
|
||||
- WORKGROUP
|
||||
7
v2rayN/v2rayN/Sample/clash_tun_yaml
Normal file
7
v2rayN/v2rayN/Sample/clash_tun_yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
tun:
|
||||
enable: true
|
||||
stack: gvisor
|
||||
dns-hijack:
|
||||
- 0.0.0.0:53
|
||||
auto-route: true
|
||||
auto-detect-interface: true
|
||||
@@ -5,28 +5,38 @@
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outboundTag": "block",
|
||||
"port": "443",
|
||||
"network": "udp"
|
||||
},
|
||||
{
|
||||
"outboundTag": "block",
|
||||
"domain": [
|
||||
"geosite:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outboundTag": "proxy",
|
||||
|
||||
"domain": [
|
||||
"geosite:gfw",
|
||||
"geosite:greatfire"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outboundTag": "proxy",
|
||||
"ip": [
|
||||
"geoip:cloudflare",
|
||||
"geoip:cloudfront",
|
||||
"1.0.0.1",
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
"8.8.4.4",
|
||||
"geoip:facebook",
|
||||
"geoip:fastly",
|
||||
"geoip:google",
|
||||
"geoip:netflix",
|
||||
"geoip:telegram",
|
||||
"geoip:twitter"
|
||||
],
|
||||
"domain": [
|
||||
"geosite:gfw",
|
||||
"geosite:greatfire",
|
||||
"geosite:tld-!cn"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
[
|
||||
{
|
||||
"outboundTag": "block",
|
||||
"port": "443",
|
||||
"network": "udp"
|
||||
},
|
||||
{
|
||||
"port": "0-65535",
|
||||
"outboundTag": "proxy"
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
"domain:example-example2.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outboundTag": "block",
|
||||
"port": "443",
|
||||
"network": "udp"
|
||||
},
|
||||
{
|
||||
"outboundTag": "block",
|
||||
"domain": [
|
||||
@@ -15,12 +20,17 @@
|
||||
{
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:cn"
|
||||
"geosite:cn",
|
||||
"geosite:geolocation-cn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outboundTag": "direct",
|
||||
"ip": [
|
||||
"223.5.5.5/32",
|
||||
"119.29.29.29/32",
|
||||
"180.76.76.76/32",
|
||||
"114.114.114.114/32",
|
||||
"geoip:private",
|
||||
"geoip:cn"
|
||||
]
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "tcp://8.8.8.8",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "proxy"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
@@ -17,16 +19,17 @@
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geosite": [
|
||||
"cn"
|
||||
"rule_set": [
|
||||
"geosite-geolocation-!cn"
|
||||
],
|
||||
"server": "local"
|
||||
"server": "remote"
|
||||
},
|
||||
{
|
||||
"geosite": [
|
||||
"category-ads-all"
|
||||
"rule_set": [
|
||||
"geosite-category-ads-all"
|
||||
],
|
||||
"server": "block"
|
||||
}
|
||||
]
|
||||
],
|
||||
"final": "local"
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
{
|
||||
"address": "223.5.5.5",
|
||||
"domains": [
|
||||
"geosite:cn"
|
||||
"geosite:cn",
|
||||
"geosite:geolocation-cn"
|
||||
],
|
||||
"expectIPs": [
|
||||
"geoip:cn"
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "tcp://8.8.8.8",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "proxy"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
@@ -17,19 +19,17 @@
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geosite": [
|
||||
"cn"
|
||||
"rule_set": [
|
||||
"geosite-geolocation-!cn"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": true
|
||||
"server": "remote"
|
||||
},
|
||||
{
|
||||
"geosite": [
|
||||
"category-ads-all"
|
||||
"rule_set": [
|
||||
"geosite-category-ads-all"
|
||||
],
|
||||
"server": "block",
|
||||
"disable_cache": true
|
||||
"server": "block"
|
||||
}
|
||||
],
|
||||
"strategy": "ipv4_only"
|
||||
"final": "local"
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace v2rayN.ViewModels
|
||||
SaveServer();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void SaveServer()
|
||||
|
||||
@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
@@ -44,7 +45,7 @@ namespace v2rayN.ViewModels
|
||||
SaveServer();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void SaveServer()
|
||||
@@ -80,7 +81,8 @@ namespace v2rayN.ViewModels
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (SelectedSource.configType != EConfigType.Socks)
|
||||
if (SelectedSource.configType != EConfigType.Socks
|
||||
&& SelectedSource.configType != EConfigType.Http)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(SelectedSource.id))
|
||||
{
|
||||
@@ -127,6 +129,7 @@ namespace v2rayN.ViewModels
|
||||
EConfigType.VMess => ConfigHandler.AddServer(_config, item),
|
||||
EConfigType.Shadowsocks => ConfigHandler.AddShadowsocksServer(_config, item),
|
||||
EConfigType.Socks => ConfigHandler.AddSocksServer(_config, item),
|
||||
EConfigType.Http => ConfigHandler.AddHttpServer(_config, item),
|
||||
EConfigType.Trojan => ConfigHandler.AddTrojanServer(_config, item),
|
||||
EConfigType.VLESS => ConfigHandler.AddVlessServer(_config, item),
|
||||
EConfigType.Hysteria2 => ConfigHandler.AddHysteria2Server(_config, item),
|
||||
|
||||
198
v2rayN/v2rayN/ViewModels/ClashConnectionsViewModel.cs
Normal file
198
v2rayN/v2rayN/ViewModels/ClashConnectionsViewModel.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.ViewModels
|
||||
{
|
||||
public class ClashConnectionsViewModel : ReactiveObject
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>();
|
||||
|
||||
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
|
||||
|
||||
[Reactive]
|
||||
public ClashConnectionModel SelectedSource { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashConnectionsViewModel()
|
||||
{
|
||||
_config = LazyConfig.Instance.GetConfig();
|
||||
SortingSelected = _config.clashUIItem.connectionsSorting;
|
||||
AutoRefresh = _config.clashUIItem.connectionsAutoRefresh;
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !string.IsNullOrEmpty(selectedSource.id));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoSortingSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.clashUIItem.connectionsAutoRefresh = AutoRefresh; });
|
||||
|
||||
ConnectionCloseCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ClashConnectionClose(false);
|
||||
}, canEditRemove);
|
||||
|
||||
ConnectionCloseAllCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ClashConnectionClose(true);
|
||||
});
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
private void DoSortingSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SortingSelected != _config.clashUIItem.connectionsSorting)
|
||||
{
|
||||
_config.clashUIItem.connectionsSorting = SortingSelected;
|
||||
}
|
||||
|
||||
GetClashConnections();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
var lastTime = DateTime.Now;
|
||||
|
||||
Observable.Interval(TimeSpan.FromSeconds(10))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!(AutoRefresh && _config.clashUIItem.showInTaskbar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var dtNow = DateTime.Now;
|
||||
if (_config.clashUIItem.connectionsRefreshInterval > 0)
|
||||
{
|
||||
if ((dtNow - lastTime).Minutes % _config.clashUIItem.connectionsRefreshInterval == 0)
|
||||
{
|
||||
GetClashConnections();
|
||||
lastTime = dtNow;
|
||||
}
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GetClashConnections()
|
||||
{
|
||||
ClashApiHandler.Instance.GetClashConnections(_config, (it) =>
|
||||
{
|
||||
if (it == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
RefreshConnections(it?.connections);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private void RefreshConnections(List<ConnectionItem>? connections)
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? [])
|
||||
{
|
||||
ClashConnectionModel model = new();
|
||||
|
||||
model.id = item.id;
|
||||
model.network = item.metadata.network;
|
||||
model.type = item.metadata.type;
|
||||
model.host = $"{(string.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
var sp = (dtNow - item.start);
|
||||
model.time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
||||
model.upload = item.upload;
|
||||
model.download = item.download;
|
||||
model.uploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
|
||||
model.downloadTraffic = $"{Utils.HumanFy((long)item.download)}";
|
||||
model.elapsed = sp.ToString(@"hh\:mm\:ss");
|
||||
model.chain = item.chains?.Count > 0 ? item.chains[0] : String.Empty;
|
||||
|
||||
lstModel.Add(model);
|
||||
}
|
||||
if (lstModel.Count <= 0) { return; }
|
||||
|
||||
//sort
|
||||
switch (SortingSelected)
|
||||
{
|
||||
case 0:
|
||||
lstModel = lstModel.OrderBy(t => t.upload / t.time).ToList();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lstModel = lstModel.OrderBy(t => t.download / t.time).ToList();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lstModel = lstModel.OrderBy(t => t.upload).ToList();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
lstModel = lstModel.OrderBy(t => t.download).ToList();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
lstModel = lstModel.OrderBy(t => t.time).ToList();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
lstModel = lstModel.OrderBy(t => t.host).ToList();
|
||||
break;
|
||||
}
|
||||
|
||||
_connectionItems.AddRange(lstModel);
|
||||
}
|
||||
|
||||
public void ClashConnectionClose(bool all)
|
||||
{
|
||||
var id = string.Empty;
|
||||
if (!all)
|
||||
{
|
||||
var item = SelectedSource;
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
id = item.id;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
}
|
||||
ClashApiHandler.Instance.ClashConnectionClose(id);
|
||||
GetClashConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
487
v2rayN/v2rayN/ViewModels/ClashProxiesViewModel.cs
Normal file
487
v2rayN/v2rayN/ViewModels/ClashProxiesViewModel.cs
Normal file
@@ -0,0 +1,487 @@
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
using static v2rayN.Models.ClashProviders;
|
||||
using static v2rayN.Models.ClashProxies;
|
||||
|
||||
namespace v2rayN.ViewModels
|
||||
{
|
||||
public class ClashProxiesViewModel : ReactiveObject
|
||||
{
|
||||
private static Config _config;
|
||||
private NoticeHandler? _noticeHandler;
|
||||
private Dictionary<String, ProxiesItem>? proxies;
|
||||
private Dictionary<String, ProvidersItem>? providers;
|
||||
private int delayTimeout = 99999999;
|
||||
|
||||
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
|
||||
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
|
||||
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedGroup { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedDetail { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelaytestCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelaytestPartCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public int RuleModeSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashProxiesViewModel()
|
||||
{
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
_config = LazyConfig.Instance.GetConfig();
|
||||
|
||||
SelectedGroup = new();
|
||||
SelectedDetail = new();
|
||||
|
||||
AutoRefresh = _config.clashUIItem.proxiesAutoRefresh;
|
||||
SortingSelected = _config.clashUIItem.proxiesSorting;
|
||||
RuleModeSelected = (int)_config.clashUIItem.ruleMode;
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && !string.IsNullOrEmpty(y.name))
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.RuleModeSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoRulemodeSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoSortingSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.clashUIItem.proxiesAutoRefresh = AutoRefresh; });
|
||||
|
||||
ProxiesReloadCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ProxiesReload();
|
||||
});
|
||||
ProxiesDelaytestCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ProxiesDelayTest(true);
|
||||
});
|
||||
|
||||
ProxiesDelaytestPartCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ProxiesDelayTest(false);
|
||||
});
|
||||
ProxiesSelectActivityCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
SetActiveProxy();
|
||||
});
|
||||
|
||||
ProxiesReload();
|
||||
DelayTestTask();
|
||||
}
|
||||
|
||||
private void DoRulemodeSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.clashUIItem.ruleMode == (ERuleMode)RuleModeSelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetRuleModeCheck((ERuleMode)RuleModeSelected);
|
||||
}
|
||||
|
||||
public void SetRuleModeCheck(ERuleMode mode)
|
||||
{
|
||||
if (_config.clashUIItem.ruleMode == mode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetRuleMode(mode);
|
||||
}
|
||||
|
||||
private void DoSortingSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SortingSelected != _config.clashUIItem.proxiesSorting)
|
||||
{
|
||||
_config.clashUIItem.proxiesSorting = SortingSelected;
|
||||
}
|
||||
|
||||
RefreshProxyDetails(c);
|
||||
}
|
||||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg, true);
|
||||
}
|
||||
|
||||
public void ProxiesReload()
|
||||
{
|
||||
GetClashProxies(true);
|
||||
ProxiesDelayTest();
|
||||
}
|
||||
|
||||
public void ProxiesClear()
|
||||
{
|
||||
proxies = null;
|
||||
providers = null;
|
||||
|
||||
ClashApiHandler.Instance.SetProxies(proxies);
|
||||
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
_proxyGroups.Clear();
|
||||
_proxyDetails.Clear();
|
||||
}));
|
||||
}
|
||||
|
||||
public void ProxiesDelayTest()
|
||||
{
|
||||
ProxiesDelayTest(true);
|
||||
}
|
||||
|
||||
#region proxy function
|
||||
|
||||
private void SetRuleMode(ERuleMode mode)
|
||||
{
|
||||
_config.clashUIItem.ruleMode = mode;
|
||||
|
||||
if (mode != ERuleMode.Unchanged)
|
||||
{
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("mode", mode.ToString().ToLower());
|
||||
ClashApiHandler.Instance.ClashConfigUpdate(headers);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetClashProxies(bool refreshUI)
|
||||
{
|
||||
ClashApiHandler.Instance.GetClashProxies(_config, (it, it2) =>
|
||||
{
|
||||
UpdateHandler(false, "Refresh Clash Proxies");
|
||||
proxies = it?.proxies;
|
||||
providers = it2?.providers;
|
||||
|
||||
ClashApiHandler.Instance.SetProxies(proxies);
|
||||
if (proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (refreshUI)
|
||||
{
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
RefreshProxyGroups();
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void RefreshProxyGroups()
|
||||
{
|
||||
var selectedName = SelectedGroup?.name;
|
||||
_proxyGroups.Clear();
|
||||
|
||||
var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (string.IsNullOrEmpty(it.name) || !proxies.ContainsKey(it.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = proxies[it.name];
|
||||
if (!Global.allowSelectType.Contains(item.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
now = item.now,
|
||||
name = item.name,
|
||||
type = item.type
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//from api
|
||||
foreach (KeyValuePair<string, ProxiesItem> kv in proxies)
|
||||
{
|
||||
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.Where(t => t.name == kv.Key).FirstOrDefault();
|
||||
if (item != null && !string.IsNullOrEmpty(item.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
now = kv.Value.now,
|
||||
name = kv.Key,
|
||||
type = kv.Value.type
|
||||
});
|
||||
}
|
||||
|
||||
if (_proxyGroups != null && _proxyGroups.Count > 0)
|
||||
{
|
||||
if (selectedName != null && _proxyGroups.Any(t => t.name == selectedName))
|
||||
{
|
||||
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.name == selectedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = _proxyGroups[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = new();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshProxyDetails(bool c)
|
||||
{
|
||||
_proxyDetails.Clear();
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup?.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
proxies.TryGetValue(name, out ProxiesItem proxy);
|
||||
if (proxy == null || proxy.all == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var lstDetails = new List<ClashProxyModel>();
|
||||
foreach (var item in proxy.all)
|
||||
{
|
||||
var isActive = item == proxy.now;
|
||||
|
||||
var proxy2 = TryGetProxy(item);
|
||||
if (proxy2 == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int delay = -1;
|
||||
if (proxy2.history.Count > 0)
|
||||
{
|
||||
delay = proxy2.history[proxy2.history.Count - 1].delay;
|
||||
}
|
||||
|
||||
lstDetails.Add(new ClashProxyModel()
|
||||
{
|
||||
isActive = isActive,
|
||||
name = item,
|
||||
type = proxy2.type,
|
||||
delay = delay <= 0 ? delayTimeout : delay,
|
||||
delayName = delay <= 0 ? string.Empty : $"{delay}ms",
|
||||
});
|
||||
}
|
||||
//sort
|
||||
switch (SortingSelected)
|
||||
{
|
||||
case 0:
|
||||
lstDetails = lstDetails.OrderBy(t => t.delay).ToList();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lstDetails = lstDetails.OrderBy(t => t.name).ToList();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_proxyDetails.AddRange(lstDetails);
|
||||
}
|
||||
|
||||
private ProxiesItem? TryGetProxy(string name)
|
||||
{
|
||||
if(proxies is null)
|
||||
return null;
|
||||
proxies.TryGetValue(name, out ProxiesItem proxy2);
|
||||
if (proxy2 != null)
|
||||
{
|
||||
return proxy2;
|
||||
}
|
||||
//from providers
|
||||
if (providers != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, ProvidersItem> kv in providers)
|
||||
{
|
||||
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
|
||||
{
|
||||
var proxy3 = kv.Value.proxies.FirstOrDefault(t => t.name == name);
|
||||
if (proxy3 != null)
|
||||
{
|
||||
return proxy3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetActiveProxy()
|
||||
{
|
||||
if (SelectedGroup == null || string.IsNullOrEmpty(SelectedGroup.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedDetail == null || string.IsNullOrEmpty(SelectedDetail.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var nameNode = SelectedDetail.name;
|
||||
if (string.IsNullOrEmpty(nameNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var selectedProxy = TryGetProxy(name);
|
||||
if (selectedProxy == null || selectedProxy.type != "Selector")
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
|
||||
selectedProxy.now = nameNode;
|
||||
var group = _proxyGroups.Where(it => it.name == SelectedGroup.name).FirstOrDefault();
|
||||
if (group != null)
|
||||
{
|
||||
group.now = nameNode;
|
||||
var group2 = JsonUtils.DeepCopy(group);
|
||||
_proxyGroups.Replace(group, group2);
|
||||
|
||||
SelectedGroup = group2;
|
||||
|
||||
//var index = _proxyGroups.IndexOf(group);
|
||||
//_proxyGroups.Remove(group);
|
||||
//_proxyGroups.Insert(index, group);
|
||||
}
|
||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||
|
||||
//RefreshProxyDetails(true);
|
||||
//GetClashProxies(true);
|
||||
}
|
||||
|
||||
private void ProxiesDelayTest(bool blAll)
|
||||
{
|
||||
UpdateHandler(false, "Clash Proxies Latency Test");
|
||||
|
||||
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
GetClashProxies(true);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
//UpdateHandler(false, $"{item.name}={result}");
|
||||
var detail = _proxyDetails.Where(it => it.name == item.name).FirstOrDefault();
|
||||
if (detail != null)
|
||||
{
|
||||
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result);
|
||||
if (dicResult != null && dicResult.ContainsKey("delay"))
|
||||
{
|
||||
detail.delay = Convert.ToInt32(dicResult["delay"].ToString());
|
||||
detail.delayName = $"{detail.delay}ms";
|
||||
}
|
||||
else if (dicResult != null && dicResult.ContainsKey("message"))
|
||||
{
|
||||
detail.delay = delayTimeout;
|
||||
detail.delayName = $"{dicResult["message"]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
detail.delay = delayTimeout;
|
||||
detail.delayName = String.Empty;
|
||||
}
|
||||
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion proxy function
|
||||
|
||||
#region task
|
||||
|
||||
public void DelayTestTask()
|
||||
{
|
||||
var lastTime = DateTime.Now;
|
||||
|
||||
Observable.Interval(TimeSpan.FromSeconds(60))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!(AutoRefresh && _config.clashUIItem.showInTaskbar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var dtNow = DateTime.Now;
|
||||
if (_config.clashUIItem.proxiesAutoDelayTestInterval > 0)
|
||||
{
|
||||
if ((dtNow - lastTime).Minutes % _config.clashUIItem.proxiesAutoDelayTestInterval == 0)
|
||||
{
|
||||
ProxiesDelayTest();
|
||||
lastTime = dtNow;
|
||||
}
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion task
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
@@ -56,7 +57,7 @@ namespace v2rayN.ViewModels
|
||||
tunDNS2 = Utils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void SaveSetting()
|
||||
|
||||
@@ -14,7 +14,9 @@ using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Handler.Fmt;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
using v2rayN.Views;
|
||||
@@ -86,6 +88,7 @@ namespace v2rayN.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> AddVlessServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddShadowsocksServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddSocksServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddHttpServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddTrojanServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
|
||||
@@ -142,22 +145,15 @@ namespace v2rayN.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> GlobalHotkeySettingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RebootAsAdminCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportOldGuiConfigCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> OpenTheFileLocationCmd { get; }
|
||||
//public ReactiveCommand<Unit, Unit> ImportOldGuiConfigCmd { get; }
|
||||
|
||||
//CheckUpdate
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
|
||||
|
||||
//public ReactiveCommand<Unit, Unit> CheckUpdateV2flyCoreCmd { get; }
|
||||
|
||||
//public ReactiveCommand<Unit, Unit> CheckUpdateSagerNetCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateXrayCoreCmd { get; }
|
||||
|
||||
//public ReactiveCommand<Unit, Unit> CheckUpdateClashCoreCmd { get; }
|
||||
//public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateSingBoxCoreCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateGeoCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
@@ -247,6 +243,9 @@ namespace v2rayN.ViewModels
|
||||
[Reactive]
|
||||
public string CurrentLanguage { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool ShowCalshUI { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
#region Init
|
||||
@@ -265,9 +264,16 @@ namespace v2rayN.ViewModels
|
||||
SelectedMoveToGroup = new();
|
||||
SelectedRouting = new();
|
||||
SelectedServer = new();
|
||||
if (_config.tunModeItem.enableTun && Utils.IsAdministrator())
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
EnableTun = true;
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
EnableTun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_config.tunModeItem.enableTun = EnableTun = false;
|
||||
}
|
||||
}
|
||||
_subId = _config.subIndexId;
|
||||
|
||||
@@ -333,6 +339,10 @@ namespace v2rayN.ViewModels
|
||||
{
|
||||
EditServer(true, EConfigType.Socks);
|
||||
});
|
||||
AddHttpServerCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
EditServer(true, EConfigType.Http);
|
||||
});
|
||||
AddTrojanServerCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
EditServer(true, EConfigType.Trojan);
|
||||
@@ -498,39 +508,31 @@ namespace v2rayN.ViewModels
|
||||
_statistics?.ClearAllServerStatistics();
|
||||
RefreshServers();
|
||||
});
|
||||
ImportOldGuiConfigCmd = ReactiveCommand.Create(() =>
|
||||
OpenTheFileLocationCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ImportOldGuiConfig();
|
||||
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}");
|
||||
});
|
||||
//ImportOldGuiConfigCmd = ReactiveCommand.Create(() =>
|
||||
//{
|
||||
// ImportOldGuiConfig();
|
||||
//});
|
||||
|
||||
//CheckUpdate
|
||||
CheckUpdateNCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateN();
|
||||
});
|
||||
//CheckUpdateV2flyCoreCmd = ReactiveCommand.Create(() =>
|
||||
//{
|
||||
// CheckUpdateCore(ECoreType.v2fly_v5);
|
||||
//});
|
||||
//CheckUpdateSagerNetCoreCmd = ReactiveCommand.Create(() =>
|
||||
//{
|
||||
// CheckUpdateCore(ECoreType.SagerNet);
|
||||
//});
|
||||
CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.Xray);
|
||||
CheckUpdateCore(ECoreType.Xray, null);
|
||||
});
|
||||
CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.mihomo, false);
|
||||
});
|
||||
//CheckUpdateClashCoreCmd = ReactiveCommand.Create(() =>
|
||||
//{
|
||||
// CheckUpdateCore(ECoreType.clash);
|
||||
//});
|
||||
//CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
|
||||
//{
|
||||
// CheckUpdateCore(ECoreType.clash_meta);
|
||||
//});
|
||||
CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.sing_box);
|
||||
CheckUpdateCore(ECoreType.sing_box, null);
|
||||
});
|
||||
CheckUpdateGeoCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
@@ -570,6 +572,7 @@ namespace v2rayN.ViewModels
|
||||
AutoHideStartup();
|
||||
|
||||
_showInTaskbar = true;
|
||||
_config.clashUIItem.showInTaskbar = _showInTaskbar;
|
||||
}
|
||||
|
||||
private void Init()
|
||||
@@ -1016,10 +1019,10 @@ namespace v2rayN.ViewModels
|
||||
{
|
||||
ShowHideWindow(false);
|
||||
|
||||
var dpiXY = Utils.GetDpiXY(Application.Current.MainWindow);
|
||||
var dpiXY = QRCodeHelper.GetDpiXY(Application.Current.MainWindow);
|
||||
string result = await Task.Run(() =>
|
||||
{
|
||||
return Utils.ScanScreen(dpiXY.Item1, dpiXY.Item2);
|
||||
return QRCodeHelper.ScanScreen(dpiXY.Item1, dpiXY.Item2);
|
||||
});
|
||||
|
||||
ShowHideWindow(true);
|
||||
@@ -1142,7 +1145,7 @@ namespace v2rayN.ViewModels
|
||||
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
var url = ShareHandler.GetShareUrl(item);
|
||||
var url = FmtHandler.GetShareUri(item);
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return;
|
||||
@@ -1283,7 +1286,7 @@ namespace v2rayN.ViewModels
|
||||
StringBuilder sb = new();
|
||||
foreach (var it in lstSelecteds)
|
||||
{
|
||||
string url = ShareHandler.GetShareUrl(it);
|
||||
string url = FmtHandler.GetShareUri(it);
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
continue;
|
||||
@@ -1308,7 +1311,7 @@ namespace v2rayN.ViewModels
|
||||
StringBuilder sb = new();
|
||||
foreach (var it in lstSelecteds)
|
||||
{
|
||||
string? url = ShareHandler.GetShareUrl(it);
|
||||
string? url = FmtHandler.GetShareUri(it);
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
continue;
|
||||
@@ -1417,32 +1420,32 @@ namespace v2rayN.ViewModels
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void ImportOldGuiConfig()
|
||||
{
|
||||
if (UI.OpenFileDialog(out string fileName,
|
||||
"guiNConfig|*.json|All|*.*") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//private void ImportOldGuiConfig()
|
||||
//{
|
||||
// if (UI.OpenFileDialog(out string fileName,
|
||||
// "guiNConfig|*.json|All|*.*") != true)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// if (Utils.IsNullOrEmpty(fileName))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
var ret = ConfigHandler.ImportOldGuiConfig(_config, fileName);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRoutingsMenu();
|
||||
InitSubscriptionView();
|
||||
RefreshServers();
|
||||
Reload();
|
||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
// var ret = ConfigHandler.ImportOldGuiConfig(_config, fileName);
|
||||
// if (ret == 0)
|
||||
// {
|
||||
// RefreshRoutingsMenu();
|
||||
// InitSubscriptionView();
|
||||
// RefreshServers();
|
||||
// Reload();
|
||||
// _noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// _noticeHandler?.Enqueue(ResUI.OperationFailed);
|
||||
// }
|
||||
//}
|
||||
|
||||
#endregion Setting
|
||||
|
||||
@@ -1461,7 +1464,7 @@ namespace v2rayN.ViewModels
|
||||
(new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateCore(ECoreType type)
|
||||
private void CheckUpdateCore(ECoreType type, bool? preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
@@ -1487,7 +1490,7 @@ namespace v2rayN.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
(new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, _config.guiItem.checkPreReleaseUpdate);
|
||||
(new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, preRelease ?? _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateGeo()
|
||||
@@ -1510,7 +1513,12 @@ namespace v2rayN.ViewModels
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
BlReloadEnabled = true;
|
||||
ShowCalshUI = (_config.runningCoreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
|
||||
if (ShowCalshUI) {
|
||||
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1555,7 +1563,7 @@ namespace v2rayN.ViewModels
|
||||
private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange)
|
||||
{
|
||||
SysProxyHandle.UpdateSysProxy(_config, _config.tunModeItem.enableTun ? true : false);
|
||||
_noticeHandler?.SendMessage(ResUI.TipChangeSystemProxy + _config.sysProxyType.ToString(), true);
|
||||
_noticeHandler?.SendMessage($"{ResUI.TipChangeSystemProxy} - {_config.sysProxyType.ToString()}", true);
|
||||
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
@@ -1681,6 +1689,7 @@ namespace v2rayN.ViewModels
|
||||
//Utile.RegWriteValue(Global.MyRegPath, Utile.WindowHwndKey, Convert.ToString((long)windowHandle));
|
||||
}
|
||||
_showInTaskbar = bl;
|
||||
_config.clashUIItem.showInTaskbar = _showInTaskbar;
|
||||
}
|
||||
|
||||
private void RestoreUI()
|
||||
@@ -1783,6 +1792,7 @@ namespace v2rayN.ViewModels
|
||||
Application.Current.Resources["StdFontSize1"] = size + 1;
|
||||
Application.Current.Resources["StdFontSize2"] = size + 2;
|
||||
Application.Current.Resources["StdFontSizeMsg"] = size - 1;
|
||||
Application.Current.Resources["StdFontSize-1"] = size - 1;
|
||||
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
@@ -1842,7 +1852,7 @@ namespace v2rayN.ViewModels
|
||||
{
|
||||
var theme = _paletteHelper.GetTheme();
|
||||
|
||||
theme.SetBaseTheme(isDarkTheme ? Theme.Dark : Theme.Light);
|
||||
theme.SetBaseTheme(isDarkTheme ? BaseTheme.Dark : BaseTheme.Light);
|
||||
_paletteHelper.SetTheme(theme);
|
||||
|
||||
Utils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme);
|
||||
|
||||
@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
@@ -20,6 +21,7 @@ namespace v2rayN.ViewModels
|
||||
[Reactive] public int localPort { get; set; }
|
||||
[Reactive] public bool udpEnabled { get; set; }
|
||||
[Reactive] public bool sniffingEnabled { get; set; }
|
||||
public IList<string> destOverride { get; set; }
|
||||
[Reactive] public bool routeOnly { get; set; }
|
||||
[Reactive] public bool allowLANConn { get; set; }
|
||||
[Reactive] public bool newPort4LAN { get; set; }
|
||||
@@ -32,8 +34,10 @@ namespace v2rayN.ViewModels
|
||||
[Reactive] public string defFingerprint { get; set; }
|
||||
[Reactive] public string defUserAgent { get; set; }
|
||||
[Reactive] public string mux4SboxProtocol { get; set; }
|
||||
[Reactive] public bool enableCacheFile4Sbox { get; set; }
|
||||
[Reactive] public int hyUpMbps { get; set; }
|
||||
[Reactive] public int hyDownMbps { get; set; }
|
||||
[Reactive] public bool enableFragment { get; set; }
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -62,9 +66,9 @@ namespace v2rayN.ViewModels
|
||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
||||
[Reactive] public bool EnableDragDropSort { get; set; }
|
||||
[Reactive] public bool DoubleClick2Activate { get; set; }
|
||||
[Reactive] public int autoUpdateInterval { get; set; }
|
||||
[Reactive] public int trayMenuServersLimit { get; set; }
|
||||
[Reactive] public string currentFontFamily { get; set; }
|
||||
[Reactive] public int AutoUpdateInterval { get; set; }
|
||||
[Reactive] public int TrayMenuServersLimit { get; set; }
|
||||
[Reactive] public string CurrentFontFamily { get; set; }
|
||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
||||
[Reactive] public string SpeedTestUrl { get; set; }
|
||||
[Reactive] public string SpeedPingTestUrl { get; set; }
|
||||
@@ -127,8 +131,10 @@ namespace v2rayN.ViewModels
|
||||
defFingerprint = _config.coreBasicItem.defFingerprint;
|
||||
defUserAgent = _config.coreBasicItem.defUserAgent;
|
||||
mux4SboxProtocol = _config.mux4SboxItem.protocol;
|
||||
enableCacheFile4Sbox = _config.coreBasicItem.enableCacheFile4Sbox;
|
||||
hyUpMbps = _config.hysteriaItem.up_mbps;
|
||||
hyDownMbps = _config.hysteriaItem.down_mbps;
|
||||
enableFragment = _config.coreBasicItem.enableFragment;
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -157,9 +163,9 @@ namespace v2rayN.ViewModels
|
||||
EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate;
|
||||
EnableDragDropSort = _config.uiItem.enableDragDropSort;
|
||||
DoubleClick2Activate = _config.uiItem.doubleClick2Activate;
|
||||
autoUpdateInterval = _config.guiItem.autoUpdateInterval;
|
||||
trayMenuServersLimit = _config.guiItem.trayMenuServersLimit;
|
||||
currentFontFamily = _config.uiItem.currentFontFamily;
|
||||
AutoUpdateInterval = _config.guiItem.autoUpdateInterval;
|
||||
TrayMenuServersLimit = _config.guiItem.trayMenuServersLimit;
|
||||
CurrentFontFamily = _config.uiItem.currentFontFamily;
|
||||
SpeedTestTimeout = _config.speedTestItem.speedTestTimeout;
|
||||
SpeedTestUrl = _config.speedTestItem.speedTestUrl;
|
||||
SpeedPingTestUrl = _config.speedTestItem.speedPingTestUrl;
|
||||
@@ -192,7 +198,7 @@ namespace v2rayN.ViewModels
|
||||
SaveSetting();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void InitCoreType()
|
||||
@@ -255,6 +261,10 @@ namespace v2rayN.ViewModels
|
||||
_noticeHandler?.Enqueue(ResUI.FillLocalListeningPort);
|
||||
return;
|
||||
}
|
||||
var needReboot = (EnableStatistics != _config.guiItem.enableStatistics
|
||||
|| EnableDragDropSort != _config.uiItem.enableDragDropSort
|
||||
|| EnableHWA != _config.guiItem.enableHWA
|
||||
|| CurrentFontFamily != _config.uiItem.currentFontFamily);
|
||||
|
||||
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
|
||||
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
|
||||
@@ -271,6 +281,7 @@ namespace v2rayN.ViewModels
|
||||
_config.inbound[0].localPort = localPort;
|
||||
_config.inbound[0].udpEnabled = udpEnabled;
|
||||
_config.inbound[0].sniffingEnabled = sniffingEnabled;
|
||||
_config.inbound[0].destOverride = destOverride?.ToList();
|
||||
_config.inbound[0].routeOnly = routeOnly;
|
||||
_config.inbound[0].allowLANConn = allowLANConn;
|
||||
_config.inbound[0].newPort4LAN = newPort4LAN;
|
||||
@@ -287,8 +298,10 @@ namespace v2rayN.ViewModels
|
||||
_config.coreBasicItem.defFingerprint = defFingerprint;
|
||||
_config.coreBasicItem.defUserAgent = defUserAgent;
|
||||
_config.mux4SboxItem.protocol = mux4SboxProtocol;
|
||||
_config.coreBasicItem.enableCacheFile4Sbox = enableCacheFile4Sbox;
|
||||
_config.hysteriaItem.up_mbps = hyUpMbps;
|
||||
_config.hysteriaItem.down_mbps = hyDownMbps;
|
||||
_config.coreBasicItem.enableFragment = enableFragment;
|
||||
|
||||
//Kcp
|
||||
//_config.kcpItem.mtu = Kcpmtu;
|
||||
@@ -309,12 +322,12 @@ namespace v2rayN.ViewModels
|
||||
_config.uiItem.enableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
|
||||
_config.guiItem.enableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
|
||||
_config.uiItem.autoHideStartup = AutoHideStartup;
|
||||
_config.guiItem.autoUpdateInterval = autoUpdateInterval;
|
||||
_config.guiItem.autoUpdateInterval = AutoUpdateInterval;
|
||||
_config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate;
|
||||
_config.uiItem.enableDragDropSort = EnableDragDropSort;
|
||||
_config.uiItem.doubleClick2Activate = DoubleClick2Activate;
|
||||
_config.guiItem.trayMenuServersLimit = trayMenuServersLimit;
|
||||
_config.uiItem.currentFontFamily = currentFontFamily;
|
||||
_config.guiItem.trayMenuServersLimit = TrayMenuServersLimit;
|
||||
_config.uiItem.currentFontFamily = CurrentFontFamily;
|
||||
_config.speedTestItem.speedTestTimeout = SpeedTestTimeout;
|
||||
_config.speedTestItem.speedTestUrl = SpeedTestUrl;
|
||||
_config.speedTestItem.speedPingTestUrl = SpeedPingTestUrl;
|
||||
@@ -337,7 +350,14 @@ namespace v2rayN.ViewModels
|
||||
|
||||
if (ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||
if (needReboot)
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.NeedRebootTips);
|
||||
}
|
||||
else
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
_view.DialogResult = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace v2rayN.ViewModels
|
||||
SaveRules();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void SaveRules()
|
||||
|
||||
@@ -4,6 +4,7 @@ using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
@@ -115,7 +116,7 @@ namespace v2rayN.ViewModels
|
||||
SaveRouting();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
public void RefreshRulesItems()
|
||||
@@ -129,6 +130,7 @@ namespace v2rayN.ViewModels
|
||||
id = item.id,
|
||||
outboundTag = item.outboundTag,
|
||||
port = item.port,
|
||||
network = item.network,
|
||||
protocols = Utils.List2String(item.protocol),
|
||||
inboundTags = Utils.List2String(item.inboundTag),
|
||||
domains = Utils.List2String(item.domain),
|
||||
@@ -196,13 +198,14 @@ namespace v2rayN.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
var lst = new List<RulesItem>();
|
||||
var lst = new List<RulesItem4Ray>();
|
||||
foreach (var it in SelectedSources)
|
||||
{
|
||||
var item = _rules.FirstOrDefault(t => t.id == it?.id);
|
||||
if (item != null)
|
||||
{
|
||||
lst.Add(item);
|
||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||
lst.Add(item2 ?? new());
|
||||
}
|
||||
}
|
||||
if (lst.Count > 0)
|
||||
@@ -288,7 +291,7 @@ namespace v2rayN.ViewModels
|
||||
|
||||
private void ImportRulesFromClipboard()
|
||||
{
|
||||
string clipboardData = Utils.GetClipboardData();
|
||||
var clipboardData = Utils.GetClipboardData();
|
||||
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace v2rayN.ViewModels
|
||||
SaveRouting();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
#region locked
|
||||
@@ -192,6 +192,7 @@ namespace v2rayN.ViewModels
|
||||
remarks = item.remarks,
|
||||
url = item.url,
|
||||
customIcon = item.customIcon,
|
||||
customRulesetPath4Singbox = item.customRulesetPath4Singbox,
|
||||
sort = item.sort,
|
||||
};
|
||||
_routingItems.Add(it);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace v2rayN.ViewModels
|
||||
SaveSub();
|
||||
});
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
private void SaveSub()
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace v2rayN.ViewModels
|
||||
SubShare();
|
||||
}, canEditRemove);
|
||||
|
||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||
Utils.SetDarkBorder(view, _config.uiItem.followSystemTheme ? !Utils.IsLightTheme() : _config.uiItem.colorModeDark);
|
||||
}
|
||||
|
||||
public void RefreshSubItems()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using v2rayN.Enums;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.ViewModels;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user