diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 394d2862..a814b8e4 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -50,7 +50,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
- name: Upload build artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v7.0.0
with:
name: v2rayN-linux
path: |
@@ -169,7 +169,7 @@ jobs:
fetch-depth: '0'
- name: Restore build artifacts
- uses: actions/download-artifact@v7
+ uses: actions/download-artifact@v8
with:
name: v2rayN-linux
path: ${{ github.workspace }}/v2rayN/Release
@@ -190,7 +190,7 @@ jobs:
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
- name: Upload RPM artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v7.0.0
with:
name: v2rayN-rpm
path: dist/rpm/**/*.rpm
diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml
index f36b2b0e..f16f28bf 100644
--- a/.github/workflows/build-osx.yml
+++ b/.github/workflows/build-osx.yml
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v7.0.0
with:
name: v2rayN-macos
path: |
diff --git a/.github/workflows/build-windows-desktop.yml b/.github/workflows/build-windows-desktop.yml
index 16f3771f..1a85e546 100644
--- a/.github/workflows/build-windows-desktop.yml
+++ b/.github/workflows/build-windows-desktop.yml
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v7.0.0
with:
name: v2rayN-windows-desktop
path: |
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index d1c2f66a..4c1ce3ea 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -42,7 +42,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
- uses: actions/upload-artifact@v6.0.0
+ uses: actions/upload-artifact@v7.0.0
with:
name: v2rayN-windows
path: |
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 57dedb74..14b16776 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -11,7 +11,7 @@
-
+
@@ -29,4 +29,4 @@
-
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Helper/DownloaderHelper.cs b/v2rayN/ServiceLib/Helper/DownloaderHelper.cs
index f4a7ae7f..71c3edbe 100644
--- a/v2rayN/ServiceLib/Helper/DownloaderHelper.cs
+++ b/v2rayN/ServiceLib/Helper/DownloaderHelper.cs
@@ -24,13 +24,13 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
- Timeout = timeout * 1000,
+ BlockTimeout = timeout * 1000,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
Headers = headers,
UserAgent = userAgent,
- Timeout = timeout * 1000,
+ ConnectTimeout = timeout * 1000,
Proxy = webProxy
}
};
@@ -62,11 +62,11 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
- Timeout = timeout * 1000,
+ BlockTimeout = timeout * 1000,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
- Timeout= timeout * 1000,
+ ConnectTimeout= timeout * 1000,
Proxy = webProxy
}
};
@@ -139,11 +139,11 @@ public class DownloaderHelper
var downloadOpt = new DownloadConfiguration()
{
- Timeout = timeout * 1000,
+ BlockTimeout = timeout * 1000,
MaxTryAgainOnFailure = 2,
RequestConfiguration =
{
- Timeout= timeout * 1000,
+ ConnectTimeout= timeout * 1000,
Proxy = webProxy
}
};
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
index b4ddaf7f..03f4e80c 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs
@@ -26,11 +26,15 @@ public partial class CoreConfigSingboxService
{
var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? [];
- useDirectDns = rules?.LastOrDefault() is { } lastRule &&
- lastRule.OutboundTag == Global.DirectTag &&
- (lastRule.Port == "0-65535" ||
- lastRule.Network == "tcp,udp" ||
- lastRule.Ip?.Contains("0.0.0.0/0") == true);
+ if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
+ {
+ var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
+ var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
+ var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0");
+ var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535";
+ var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp";
+ useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork;
+ }
}
_coreConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
var simpleDnsItem = context.SimpleDnsItem;
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
index 40f8951d..b30c9c48 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
@@ -20,10 +20,13 @@ public partial class CoreConfigSingboxService
{
proxyOutboundList.AddRange(BuildGroupProxyOutbounds(baseTagName));
}
- var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList();
- if (proxyTags.Count > 1)
+ if (withSelector)
{
- proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName));
+ var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList();
+ if (proxyTags.Count > 1)
+ {
+ proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName));
+ }
}
return proxyOutboundList;
}
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
index f4c9c852..e34dceb6 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
@@ -60,6 +60,12 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
GenStatistic();
+ var finalRule = BuildFinalRule();
+ if (!string.IsNullOrEmpty(finalRule?.balancerTag))
+ {
+ _coreConfig.routing.rules.Add(finalRule);
+ }
+
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = ApplyFullConfigTemplate();
@@ -234,6 +240,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
GenLog();
GenOutbounds();
+ _coreConfig.routing.domainStrategy = Global.AsIs;
_coreConfig.routing.rules.Clear();
_coreConfig.inbounds.Clear();
_coreConfig.inbounds.Add(new()
@@ -244,6 +251,8 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
protocol = EInboundProtocol.mixed.ToString(),
});
+ _coreConfig.routing.rules.Add(BuildFinalRule());
+
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(_coreConfig);
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
index 10cd905b..1d31f7b2 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
@@ -70,17 +70,16 @@ public partial class CoreConfigV2rayService
dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null;
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
- if (_coreConfig.routing.domainStrategy == Global.IPIfNonMatch)
+ // DNS routing
+ var finalRule = BuildFinalRule();
+ dnsItem.tag = Global.DnsTag;
+ _coreConfig.routing.rules.Add(new()
{
- // DNS routing
- dnsItem.tag = Global.DnsTag;
- _coreConfig.routing.rules.Add(new RulesItem4Ray
- {
- type = "field",
- inboundTag = new List { Global.DnsTag },
- outboundTag = Global.ProxyTag,
- });
- }
+ type = "field",
+ inboundTag = [Global.DnsTag],
+ outboundTag = finalRule.outboundTag,
+ balancerTag = finalRule.balancerTag
+ });
_coreConfig.dns = dnsItem;
}
@@ -93,45 +92,6 @@ public partial class CoreConfigV2rayService
private void FillDnsServers(Dns4Ray dnsItem)
{
var simpleDNSItem = context.SimpleDnsItem;
- static List ParseDnsAddresses(string? dnsInput, string defaultAddress)
- {
- var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
- .Select(addr => addr.Trim())
- .Where(addr => !string.IsNullOrEmpty(addr))
- .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
- .Distinct()
- .ToList() ?? new List { defaultAddress };
- return addresses.Count > 0 ? addresses : new List { defaultAddress };
- }
-
- static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null)
- {
- var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
- var domainFinal = dnsAddress;
- int? portFinal = null;
- if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
- {
- domainFinal = domain;
- portFinal = port > 0 ? port : null;
- }
- else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
- {
- domainFinal = scheme + "://" + domain;
- portFinal = port > 0 ? port : null;
- }
- var dnsServer = new DnsServer4Ray
- {
- address = domainFinal,
- port = portFinal,
- skipFallback = true,
- domains = domains.Count > 0 ? domains : null,
- expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
- };
- return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
- {
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
- });
- }
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First());
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First());
@@ -252,17 +212,6 @@ public partial class CoreConfigV2rayService
dnsItem.servers ??= [];
- void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null)
- {
- if (domains.Count > 0)
- {
- foreach (var dnsAddress in dnsAddresses)
- {
- dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
- }
- }
- }
-
AddDnsServers(remoteDNSAddress, proxyDomainList);
AddDnsServers(directDNSAddress, directDomainList);
AddDnsServers(remoteDNSAddress, proxyGeositeList);
@@ -273,14 +222,73 @@ public partial class CoreConfigV2rayService
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
}
- var useDirectDns = rules?.LastOrDefault() is { } lastRule
- && lastRule.OutboundTag == Global.DirectTag
- && (lastRule.Port == "0-65535"
- || lastRule.Network == "tcp,udp"
- || lastRule.Ip?.Contains("0.0.0.0/0") == true);
+ var useDirectDns = false;
+
+ if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
+ {
+ var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
+ var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
+ var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0");
+ var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535";
+ var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp";
+ useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork;
+ }
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
dnsItem.servers.AddRange(defaultDnsServers);
+ return;
+
+ static List ParseDnsAddresses(string? dnsInput, string defaultAddress)
+ {
+ var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
+ .Select(addr => addr.Trim())
+ .Where(addr => !string.IsNullOrEmpty(addr))
+ .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
+ .Distinct()
+ .ToList() ?? [defaultAddress];
+ return addresses.Count > 0 ? addresses : new List { defaultAddress };
+ }
+
+ static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null)
+ {
+ var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
+ var domainFinal = dnsAddress;
+ int? portFinal = null;
+ if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
+ {
+ domainFinal = domain;
+ portFinal = port > 0 ? port : null;
+ }
+ else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
+ {
+ domainFinal = scheme + "://" + domain;
+ portFinal = port > 0 ? port : null;
+ }
+ var dnsServer = new DnsServer4Ray
+ {
+ address = domainFinal,
+ port = portFinal,
+ skipFallback = true,
+ domains = domains.Count > 0 ? domains : null,
+ expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
+ };
+ return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
+ {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ });
+ }
+
+ void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null)
+ {
+ if (domains.Count <= 0)
+ {
+ return;
+ }
+ foreach (var dnsAddress in dnsAddresses)
+ {
+ dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
+ }
+ }
}
private void FillDnsHosts(Dns4Ray dnsItem)
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs
index 1d3d1a37..76969297 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs
@@ -181,4 +181,28 @@ public partial class CoreConfigV2rayService
return tag;
}
+
+ private RulesItem4Ray BuildFinalRule()
+ {
+ var finalRule = new RulesItem4Ray()
+ {
+ type = "field",
+ network = "tcp,udp",
+ outboundTag = Global.ProxyTag,
+ };
+ var balancer =
+ _coreConfig?.routing?.balancers?.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null);
+ var domainStrategy = _coreConfig.routing?.domainStrategy ?? Global.AsIs;
+ if (balancer is not null)
+ {
+ finalRule.outboundTag = null;
+ finalRule.balancerTag = balancer.tag;
+ }
+ if (domainStrategy == Global.IPIfNonMatch)
+ {
+ finalRule.network = null;
+ finalRule.ip = ["0.0.0.0/0", "::/0"];
+ }
+ return finalRule;
+ }
}