Merge branch '2dust:master' into master

This commit is contained in:
JieXu 2026-05-06 18:27:13 +08:00 committed by GitHub
commit 7f072f7dc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 468 additions and 102 deletions

29
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Code Test
on:
pull_request:
branches:
- master
paths:
- 'v2rayN/ServiceLib/Services/CoreConfig/**'
- 'v2rayN/ServiceLib/Handler/Fmt/**'
- '.github/workflows/test.yml'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup .NET
uses: actions/setup-dotnet@v5.2.0
with:
dotnet-version: '8.0.x'
- name: Test Code
working-directory: ./v2rayN
run: dotnet test ./ServiceLib.Tests

View file

@ -0,0 +1,47 @@
using AwesomeAssertions;
using ServiceLib.Handler.Fmt;
using Xunit;
namespace ServiceLib.Tests.Fmt;
public class WireguardFmtTests
{
[Fact]
public void ResolveConfig_ShouldParsePeersAndIgnoreInlineComments()
{
const string config =
"""
[Interface]
PrivateKey = interface-private-key
Address = 10.0.0.2/32, fd00::2/128 ; inline comment
MTU = 1420
[Peer]
PublicKey = peer-public-key
PresharedKey = peer-preshared-key
Reserved = 1, 2, 3 # inline comment
Endpoint = [2001:db8::1]:51820 # inline comment
[Peer]
PublicKey = peer-public-key-2
Endpoint = example.com:12345
""";
var resolved = WireguardFmt.ResolveConfig(config);
resolved.Should().NotBeNull();
resolved.Should().HaveCount(2);
var first = resolved![0];
first.Address.Should().Be("2001:db8::1");
first.Port.Should().Be(51820);
first.Password.Should().Be("interface-private-key");
first.GetProtocolExtra().WgReserved.Should().Be("1, 2, 3");
first.GetProtocolExtra().WgInterfaceAddress.Should().Be("10.0.0.2/32, fd00::2/128");
first.GetProtocolExtra().WgMtu.Should().Be(1420);
var second = resolved[1];
second.Address.Should().Be("example.com");
second.Port.Should().Be(12345);
}
}

View file

@ -17,6 +17,13 @@ public class JsonUtils
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonSerializerOptions _defaultSerializeNoIndentedOptions = new()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
{
WriteIndented = true,
@ -24,6 +31,13 @@ public class JsonUtils
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonSerializerOptions _nullValueSerializeNoIndentedOptions = new()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
{
CommentHandling = JsonCommentHandling.Skip
@ -104,7 +118,13 @@ public class JsonUtils
{
return result;
}
var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions;
var options = (nullValue, indented) switch
{
(true, true) => _nullValueSerializeOptions,
(true, false) => _nullValueSerializeNoIndentedOptions,
(false, true) => _defaultSerializeOptions,
_ => _defaultSerializeNoIndentedOptions
};
result = JsonSerializer.Serialize(obj, options);
}
catch (Exception ex)

View file

@ -48,6 +48,7 @@ public class CoreConfigContextBuilder
RawDnsItem = await AppManager.Instance.GetDNSItem(coreType),
RoutingItem = await ConfigHandler.GetDefaultRouting(config),
IsWindows = Utils.IsWindows(),
IsMacOS = Utils.IsMacOS(),
};
var validatorResult = NodeValidatorResult.Empty();
var (actNode, nodeValidatorResult) = await ResolveNodeAsync(context, node);

View file

@ -790,12 +790,30 @@ public static class ConfigHandler
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Password = profileItem.Password.TrimEx();
var wgReserved = profileItem.GetProtocolExtra().WgReserved?.TrimEx();
if (!wgReserved.IsNullOrEmpty()
&& !wgReserved.Contains(','))
{
// Base64 format, convert to standard format
try
{
var bytes = Convert.FromBase64String(wgReserved);
var reserved = new byte[3];
Array.Copy(bytes, reserved, Math.Min(bytes.Length, 3));
wgReserved = string.Join(", ", reserved);
}
catch
{
// If conversion fails, keep the original value
}
}
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
{
WgPublicKey = profileItem.GetProtocolExtra().WgPublicKey?.TrimEx(),
WgPresharedKey = profileItem.GetProtocolExtra().WgPresharedKey?.TrimEx(),
WgInterfaceAddress = profileItem.GetProtocolExtra().WgInterfaceAddress?.TrimEx(),
WgReserved = profileItem.GetProtocolExtra().WgReserved?.TrimEx(),
WgReserved = wgReserved,
WgMtu = profileItem.GetProtocolExtra().WgMtu is null or <= 0 ? Global.TunMtus.First() : profileItem.GetProtocolExtra().WgMtu,
});
@ -1639,30 +1657,18 @@ public static class ConfigHandler
ProfileItem? profileItem = null;
//Is sing-box configuration
if (profileItem is null)
{
profileItem = SingboxFmt.ResolveFull(strData, subRemarks);
}
profileItem ??= SingboxFmt.ResolveFull(strData, subRemarks);
//Is v2ray configuration
if (profileItem is null)
{
profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
}
profileItem ??= V2rayFmt.ResolveFull(strData, subRemarks);
//Is Html Page
if (profileItem is null && HtmlPageFmt.IsHtmlPage(strData))
{
return -1;
}
//Is Clash configuration
if (profileItem is null)
{
profileItem = ClashFmt.ResolveFull(strData, subRemarks);
}
profileItem ??= ClashFmt.ResolveFull(strData, subRemarks);
//Is hysteria configuration
if (profileItem is null)
{
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
}
profileItem ??= Hysteria2Fmt.ResolveFull2(strData, subRemarks);
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
{
return -1;
@ -1727,6 +1733,40 @@ public static class ConfigHandler
return -1;
}
private static async Task<int> AddBatchServers4Wireguard(Config config, string strData, string subid, bool isSub)
{
if (strData.IsNullOrEmpty())
{
return -1;
}
if (!(strData.Contains("[Interface]", StringComparison.OrdinalIgnoreCase)
&& strData.Contains("[Peer]", StringComparison.OrdinalIgnoreCase)))
{
return -1;
}
if (isSub && subid.IsNotEmpty())
{
await RemoveServersViaSubid(config, subid, isSub);
}
var lstServer = WireguardFmt.ResolveConfig(strData);
if (lstServer?.Count > 0)
{
var counter = 0;
foreach (var item in lstServer)
{
item.Subid = subid;
item.IsSub = isSub;
if (await AddWireguardServer(config, item) == 0)
{
counter++;
}
}
await SaveConfig(config);
return counter;
}
return -1;
}
/// <summary>
/// Main entry point for adding batch servers from various formats
/// Tries different parsing methods to import as many servers as possible
@ -1769,6 +1809,12 @@ public static class ConfigHandler
counter = await AddBatchServers4SsSIP008(config, strData, subid, isSub);
}
//maybe wireguard config
if (counter < 1)
{
counter = await AddBatchServers4Wireguard(config, strData, subid, isSub);
}
//maybe other sub
if (counter < 1)
{

View file

@ -27,9 +27,10 @@ public class WireguardFmt : BaseFmt
item.SetProtocolExtra(item.GetProtocolExtra() with
{
WgPublicKey = GetQueryDecoded(query, "publickey"),
WgPresharedKey = GetQueryDecoded(query, "presharedkey"),
WgReserved = GetQueryDecoded(query, "reserved"),
WgInterfaceAddress = GetQueryDecoded(query, "address"),
WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtuVal) ? mtuVal : 1280,
WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtuVal) ? mtuVal : null,
});
return item;
@ -48,20 +49,183 @@ public class WireguardFmt : BaseFmt
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var protoExtra = item.GetProtocolExtra();
var dicQuery = new Dictionary<string, string>();
if (!item.GetProtocolExtra().WgPublicKey.IsNullOrEmpty())
if (!protoExtra.WgPublicKey.IsNullOrEmpty())
{
dicQuery.Add("publickey", Utils.UrlEncode(item.GetProtocolExtra().WgPublicKey));
dicQuery.Add("publickey", Utils.UrlEncode(protoExtra.WgPublicKey));
}
if (!item.GetProtocolExtra().WgReserved.IsNullOrEmpty())
if (!protoExtra.WgPresharedKey.IsNullOrEmpty())
{
dicQuery.Add("reserved", Utils.UrlEncode(item.GetProtocolExtra().WgReserved));
dicQuery.Add("presharedkey", Utils.UrlEncode(protoExtra.WgPresharedKey));
}
if (!item.GetProtocolExtra().WgInterfaceAddress.IsNullOrEmpty())
if (!protoExtra.WgReserved.IsNullOrEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(item.GetProtocolExtra().WgInterfaceAddress));
dicQuery.Add("reserved", Utils.UrlEncode(protoExtra.WgReserved));
}
if (!protoExtra.WgInterfaceAddress.IsNullOrEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(protoExtra.WgInterfaceAddress));
}
if (protoExtra.WgMtu > 0)
{
dicQuery.Add("mtu", protoExtra.WgMtu.ToString());
}
dicQuery.Add("mtu", Utils.UrlEncode(item.GetProtocolExtra().WgMtu > 0 ? item.GetProtocolExtra().WgMtu.ToString() : "1280"));
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Password, dicQuery, remark);
}
public static List<ProfileItem>? ResolveConfig(string strData)
{
var interfaceDic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var peerDicList = new List<Dictionary<string, string>>();
var currentDicRef = interfaceDic;
using (var reader = new StringReader(strData))
{
while (reader.ReadLine() is { } line)
{
if (line.IsNullOrEmpty())
{
continue;
}
var trimmedLine = line.Trim();
if (trimmedLine.Equals("[Interface]", StringComparison.OrdinalIgnoreCase))
{
currentDicRef = interfaceDic;
continue;
}
if (trimmedLine.Equals("[Peer]", StringComparison.OrdinalIgnoreCase))
{
var peerDic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
peerDicList.Add(peerDic);
currentDicRef = peerDic;
continue;
}
if (trimmedLine.StartsWith('[') || trimmedLine.StartsWith('#') || trimmedLine.StartsWith(';'))
{
continue;
}
var idx = line.IndexOf('=');
if (idx <= 0)
{
continue;
}
var key = line[..idx].Trim();
var value = line[(idx + 1)..].Trim();
var commentPos = value.IndexOfAny(['#', ';']);
if (commentPos >= 0)
{
value = value[..commentPos].TrimEnd();
}
currentDicRef[key] = value;
}
}
if (!interfaceDic.TryGetValue("PrivateKey", out var privateKey) || privateKey.IsNullOrEmpty())
{
return null;
}
var wgMtu = interfaceDic.TryGetValue("MTU", out var mtuStr) && int.TryParse(mtuStr, out var mtuVal) ? mtuVal : 0;
var wgInterfaceAddress = interfaceDic.TryGetValue("Address", out var interfaceAddress) ? interfaceAddress : string.Empty;
var index = 0;
var resultList = new List<ProfileItem>();
foreach (var peerDic in peerDicList)
{
if (!peerDic.TryGetValue("Endpoint", out var endpoint) || endpoint.IsNullOrEmpty())
{
continue;
}
if (!TryParseEndpoint(endpoint, out var peerAddress, out var peerPort))
{
continue;
}
var protoExtra = new ProtocolExtraItem
{
WgPublicKey = (peerDic.TryGetValue("PublicKey", out var publicKey) ? publicKey : string.Empty).NullIfEmpty(),
WgPresharedKey = (peerDic.TryGetValue("PresharedKey", out var presharedKey) ? presharedKey : string.Empty).NullIfEmpty(),
WgInterfaceAddress = wgInterfaceAddress,
WgReserved = (peerDic.TryGetValue("Reserved", out var reserved) ? reserved : string.Empty).NullIfEmpty(),
WgMtu = wgMtu > 0 ? wgMtu : null,
};
var item = new ProfileItem
{
Remarks = $"{nameof(EConfigType.WireGuard)} Peer {index + 1}",
ConfigType = EConfigType.WireGuard,
Address = peerAddress,
Port = peerPort,
Password = privateKey,
};
item.SetProtocolExtra(protoExtra);
resultList.Add(item);
index += 1;
}
return resultList;
}
private static bool TryParseEndpoint(string endpoint, out string address, out int port)
{
address = string.Empty;
port = 2408;
var trimmedEndpoint = endpoint.Trim();
if (trimmedEndpoint.IsNullOrEmpty())
{
return false;
}
if (trimmedEndpoint[0] == '[')
{
var closeIndex = trimmedEndpoint.IndexOf(']');
if (closeIndex <= 1)
{
return false;
}
address = trimmedEndpoint[1..closeIndex].Trim();
var portIndex = closeIndex + 1;
if (portIndex < trimmedEndpoint.Length && trimmedEndpoint[portIndex] == ':' &&
int.TryParse(trimmedEndpoint[(portIndex + 1)..].Trim(), out var bracketedPort) && bracketedPort is > 0 and <= 65535)
{
port = bracketedPort;
}
return address.IsNotEmpty();
}
var lastColonIndex = trimmedEndpoint.LastIndexOf(':');
if (lastColonIndex <= 0)
{
address = trimmedEndpoint;
return true;
}
address = trimmedEndpoint[..lastColonIndex].Trim();
var portText = trimmedEndpoint[(lastColonIndex + 1)..].Trim();
if (address.IsNullOrEmpty())
{
return false;
}
if (int.TryParse(portText, out var parsedPortValue) && parsedPortValue is > 0 and <= 65535)
{
port = parsedPortValue;
return true;
}
address = trimmedEndpoint;
return true;
}
}

View file

@ -19,4 +19,5 @@ public record CoreConfigContext
public HashSet<string> ProtectDomainList { get; init; } = [];
public bool IsWindows { get; init; }
public bool IsMacOS { get; init; }
}

View file

@ -173,7 +173,7 @@ public class Peer4Sbox
public string? pre_shared_key { get; set; }
public List<string> allowed_ips { get; set; }
public int? persistent_keepalive_interval { get; set; }
public List<int> reserved { get; set; }
public List<int>? reserved { get; set; }
}
public class Tls4Sbox

View file

@ -163,6 +163,7 @@ public class WireguardPeer4Ray
{
public string endpoint { get; set; }
public string publicKey { get; set; }
public string? preSharedKey { get; set; }
}
public class VnextItem4Ray

View file

@ -3222,6 +3222,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 MTU 的本地化字符串。
/// </summary>
public static string TbMtu {
get {
return ResourceManager.GetString("TbMtu", resourceCulture);
}
}
/// <summary>
/// 查找类似 Transport protocol(network) 的本地化字符串。
/// </summary>
@ -3312,6 +3321,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 PreSharedKey 的本地化字符串。
/// </summary>
public static string TbPreSharedKey {
get {
return ResourceManager.GetString("TbPreSharedKey", resourceCulture);
}
}
/// <summary>
/// 查找类似 Socks port 的本地化字符串。
/// </summary>
@ -3430,7 +3448,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Reserved (2,3,4) 的本地化字符串。
/// 查找类似 Reserved 的本地化字符串。
/// </summary>
public static string TbReserved {
get {
@ -4365,15 +4383,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 MTU 的本地化字符串。
/// </summary>
public static string TbSettingsTunMtu {
get {
return ResourceManager.GetString("TbSettingsTunMtu", resourceCulture);
}
}
/// <summary>
/// 查找类似 Stack 的本地化字符串。
/// </summary>

View file

@ -1062,7 +1062,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>پشته شبکه</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1075,7 +1075,7 @@
<value>کلید خصوصی</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved (2,3,4)</value>
<value>Reserved</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>آدرس (IPv4, IPv6)</value>
@ -1725,4 +1725,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>For multi-interface environments, enter the name of the interface to bind. Only effective on Windows systems and TUN mode</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1059,7 +1059,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Pile de protocoles</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1072,7 +1072,7 @@
<value>PrivateKey</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved (2,3,4)</value>
<value>Reserved</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address (IPv4,IPv6)</value>
@ -1716,4 +1716,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>Pour les environnements multi-interfaces, entrez le nom de l'interface à lier. Ne fonctionne que sur les systèmes Windows et en mode TUN</value>
</data>
<data name="menuUdpTestServer" xml:space="preserve">
<value>Test Configurations UDP Delay</value>
</data>
<data name="TbSettingsUdpTestUrl" xml:space="preserve">
<value>UDP Test Url</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1062,7 +1062,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Hálózati verem</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1075,7 +1075,7 @@
<value>Privát kulcs</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Fenntartott (2,3,4)</value>
<value>Fenntartott</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Cím (IPv4, IPv6)</value>
@ -1725,4 +1725,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>For multi-interface environments, enter the name of the interface to bind. Only effective on Windows systems and TUN mode</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1062,7 +1062,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Stack</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1075,7 +1075,7 @@
<value>Private Key</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved (2,3,4)</value>
<value>Reserved</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address (IPv4, IPv6)</value>
@ -1725,4 +1725,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>For multi-interface environments, enter the name of the interface to bind. Only effective on Windows systems and TUN mode</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1062,7 +1062,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Сетевой стек</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1075,7 +1075,7 @@
<value>Приватный ключ</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Зарезервировано (2, 3, 4)</value>
<value>Зарезервировано</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Адрес (IPv4, IPv6)</value>
@ -1725,4 +1725,7 @@
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>Для среды с несколькими сетевыми интерфейсами укажите имя интерфейса для привязки. Работает только в Windows и режиме TUN</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1059,7 +1059,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>协议栈</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1072,7 +1072,7 @@
<value>PrivateKey</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved (2,3,4)</value>
<value>Reserved</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address (IPv4,IPv6)</value>
@ -1722,4 +1722,7 @@
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>用于多网口环境,填写要绑定的网口名称,仅生效于 Windows 系统和 TUN 模式</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -1059,7 +1059,7 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>協定堆疊</value>
</data>
<data name="TbSettingsTunMtu" xml:space="preserve">
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
@ -1072,7 +1072,7 @@
<value>PrivateKey</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved (2,3,4)</value>
<value>Reserved</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address (Ipv4,Ipv6)</value>
@ -1722,4 +1722,7 @@
<data name="TbSettingsBindInterfaceTip" xml:space="preserve">
<value>For multi-interface environments, enter the name of the interface to bind. Only effective on Windows systems and TUN mode</value>
</data>
<data name="TbPreSharedKey" xml:space="preserve">
<value>PreSharedKey</value>
</data>
</root>

View file

@ -62,7 +62,7 @@ public partial class CoreConfigSingboxService
}
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
tunInbound.interface_name = Utils.IsMacOS() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.interface_name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;

View file

@ -309,7 +309,7 @@ public partial class CoreConfigSingboxService
{
var protocolExtra = _node.GetProtocolExtra();
endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress);
endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"];
endpoint.type = Global.ProtocolTypes[_node.ConfigType];
switch (_node.ConfigType)
@ -318,13 +318,12 @@ public partial class CoreConfigSingboxService
{
var peer = new Peer4Sbox
{
public_key = protocolExtra.WgPublicKey,
public_key = protocolExtra.WgPublicKey ?? string.Empty,
pre_shared_key = protocolExtra.WgPresharedKey,
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(s => s.Trim()).Select(int.Parse).ToList(),
address = _node.Address,
port = _node.Port,
// TODO default ["0.0.0.0/0", "::/0"]
allowed_ips = new() { "0.0.0.0/0", "::/0" },
allowed_ips = ["0.0.0.0/0", "::/0"],
};
endpoint.private_key = _node.Password;
endpoint.mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First();

View file

@ -200,9 +200,9 @@ public partial class CoreConfigV2rayService
if (normalizedDomain.StartsWith(Global.GeoSitePrefix) || normalizedDomain.StartsWith("ext:"))
{
var isExpectedDomain = !regionName.IsNullOrEmpty()
|| normalizedDomain.EndsWith($"-{regionName}")
&& (normalizedDomain.EndsWith($"-{regionName}")
|| normalizedDomain.EndsWith($"@{regionName}")
|| normalizedDomain == Global.GeoSitePrefix + regionName;
|| normalizedDomain == Global.GeoSitePrefix + regionName);
var targetList = isExpectedDomain ? expectedDomainList : directGeositeList;
targetList.Add(normalizedDomain);
}

View file

@ -54,7 +54,7 @@ public partial class CoreConfigV2rayService
_config.TunModeItem.Mtu = Global.TunMtus.First();
}
var tunInbound = JsonUtils.Deserialize<Inbounds4Ray>(EmbedUtils.GetEmbedText(Global.V2raySampleTunInbound)) ?? new Inbounds4Ray { };
tunInbound.settings.name = Utils.IsMacOS() ? $"utun{new Random().Next(99)}" : "xray_tun";
tunInbound.settings.name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "xray_tun";
tunInbound.settings.MTU = _config.TunModeItem.Mtu;
if (_config.TunModeItem.EnableIPv6Address == false)
{

View file

@ -258,13 +258,14 @@ public partial class CoreConfigV2rayService
var peer = new WireguardPeer4Ray
{
publicKey = protocolExtra.WgPublicKey ?? "",
endpoint = address + ":" + _node.Port.ToString()
endpoint = address + ":" + _node.Port.ToString(),
preSharedKey = protocolExtra.WgPresharedKey,
};
var setting = new Outboundsettings4Ray
{
address = Utils.String2List(protocolExtra.WgInterfaceAddress),
address = Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"],
secretKey = _node.Password,
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(s => s.Trim()).Select(int.Parse).ToList(),
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
peers = [peer]
};

View file

@ -53,8 +53,9 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public string WgPublicKey { get; set; }
//[Reactive]
//public string WgPresharedKey { get; set; }
[Reactive]
public string WgPresharedKey { get; set; }
[Reactive]
public string WgInterfaceAddress { get; set; }
@ -159,17 +160,8 @@ public class AddServerViewModel : MyReactiveObject
switch (SelectedSource.GetNetwork())
{
case nameof(ETransport.raw):
Host = value;
break;
case nameof(ETransport.ws):
Host = value;
break;
case nameof(ETransport.httpupgrade):
Host = value;
break;
case nameof(ETransport.xhttp):
Host = value;
break;
@ -202,13 +194,7 @@ public class AddServerViewModel : MyReactiveObject
break;
case nameof(ETransport.ws):
Path = value;
break;
case nameof(ETransport.httpupgrade):
Path = value;
break;
case nameof(ETransport.xhttp):
Path = value;
break;
@ -303,6 +289,7 @@ public class AddServerViewModel : MyReactiveObject
VlessEncryption = protocolExtra.VlessEncryption?.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None;
SsMethod = protocolExtra.SsMethod ?? string.Empty;
WgPublicKey = protocolExtra.WgPublicKey ?? string.Empty;
WgPresharedKey = protocolExtra.WgPresharedKey ?? string.Empty;
WgInterfaceAddress = protocolExtra.WgInterfaceAddress ?? string.Empty;
WgReserved = protocolExtra.WgReserved ?? string.Empty;
WgMtu = protocolExtra.WgMtu ?? 1280;
@ -401,6 +388,7 @@ public class AddServerViewModel : MyReactiveObject
VlessEncryption = VlessEncryption.NullIfEmpty(),
SsMethod = SsMethod.NullIfEmpty(),
WgPublicKey = WgPublicKey.NullIfEmpty(),
WgPresharedKey = WgPresharedKey.NullIfEmpty(),
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
WgReserved = WgReserved.NullIfEmpty(),
WgMtu = WgMtu >= 576 ? WgMtu : null,

View file

@ -508,7 +508,7 @@
Grid.Row="2"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
@ -542,43 +542,57 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbReserved}" />
Text="{x:Static resx:ResUI.TbPreSharedKey}" />
<TextBox
x:Name="txtPath9"
x:Name="txtPreSharedKey9"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Watermark="2,3,4" />
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbReserved}" />
<TextBox
x:Name="txtPath9"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Watermark="0, 0, 0" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbLocalAddress}" />
<TextBox
x:Name="txtRequestHost9"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Watermark="Ipv4,Ipv6" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
Text="{x:Static resx:ResUI.TbMtu}" />
<TextBox
x:Name="txtShortId9"
Grid.Row="5"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Watermark="1500" />
Watermark="1280" />
</Grid>
<Grid
x:Name="gridAnytls"
@ -850,7 +864,7 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="MTU" />
Text="{x:Static resx:ResUI.TbMtu}" />
<TextBox
x:Name="txtKcpMtu"
Grid.Row="1"

View file

@ -185,6 +185,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.WireGuard:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);

View file

@ -869,7 +869,7 @@
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
Text="{x:Static resx:ResUI.TbMtu}" />
<ComboBox
x:Name="cmbMtu"
Grid.Row="5"

View file

@ -678,6 +678,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
@ -721,14 +722,14 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbReserved}" />
Text="{x:Static resx:ResUI.TbPreSharedKey}" />
<TextBox
x:Name="txtPath9"
x:Name="txtPreSharedKey9"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
materialDesign:HintAssist.Hint="2,3,4"
HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" />
<TextBlock
@ -737,10 +738,26 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbReserved}" />
<TextBox
x:Name="txtPath9"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
materialDesign:HintAssist.Hint="0, 0, 0"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbLocalAddress}" />
<TextBox
x:Name="txtRequestHost9"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
@ -748,20 +765,20 @@
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="5"
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
Text="{x:Static resx:ResUI.TbMtu}" />
<TextBox
x:Name="txtShortId9"
Grid.Row="5"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
materialDesign:HintAssist.Hint="1500"
materialDesign:HintAssist.Hint="1280"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid
@ -1119,7 +1136,7 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="MTU" />
Text="{x:Static resx:ResUI.TbMtu}" />
<TextBox
x:Name="txtKcpMtu"
Grid.Row="1"

View file

@ -184,6 +184,7 @@ public partial class AddServerWindow
case EConfigType.WireGuard:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);

View file

@ -469,7 +469,7 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
Text="{x:Static resx:ResUI.TbMtu}" />
<TextBox Style="{StaticResource DefTextBox}"
x:Name="txtKcpmtu"
Grid.Row="1"
@ -1133,7 +1133,7 @@
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
Text="{x:Static resx:ResUI.TbMtu}" />
<ComboBox
x:Name="cmbMtu"
Grid.Row="5"