From 9c7c1690563ca099d1e7c5deb0843356e8a6473e Mon Sep 17 00:00:00 2001 From: Henglie <90338155+Henglie@users.noreply.github.com> Date: Sat, 14 Nov 2026 10:53:16 +0800 Subject: [PATCH] fix: inherit targetStrategy for custom outbounds (#10597) GenDns applies the DNS Strategy4Proxy setting to the generated proxy outbound, but ApplyCustomOutboundReplace replaces the whole JSON object and dropped targetStrategy when the custom outbound did not define one. Keep the generated value unless the custom JSON sets it explicitly. Add regression tests for the inherited and explicitly configured cases. --- .../V2ray/CoreConfigV2rayServiceTests.cs | 75 +++++++++++++++++++ .../V2ray/V2rayConfigTemplateService.cs | 7 ++ 2 files changed, 82 insertions(+) diff --git a/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs b/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs index f9c4ddea..08b182ce 100644 --- a/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs +++ b/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs @@ -857,6 +857,81 @@ public class CoreConfigV2rayServiceTests await proxyOutbound!.protocol.Should().BeEqualTo("shadowsocks"); } + [Test] + public async Task GenerateClientConfigContent_CustomOutbound_ShouldInheritTargetStrategyFromDnsSettings() + { + var config = CoreConfigTestFactory.CreateConfig(ECoreType.Xray); + config.SimpleDNSItem.Strategy4Proxy = "UseIPv4"; + CoreConfigTestFactory.BindAppManagerConfig(config); + + var customNode = CoreConfigTestFactory.CreateCustomOutboundNode(ECoreType.Xray, "n-custom", "custom-xray"); + var customJsonContent = """ + { + "protocol": "shadowsocks", + "settings": { + "servers": [ + { + "address": "1.2.3.4", + "port": 8388, + "method": "aes-128-gcm", + "password": "custom_password" + } + ] + } + } + """; + + var context = CoreConfigTestFactory.CreateContext(config, customNode, ECoreType.Xray); + context.CustomOutboundContent[customNode.IndexId] = customJsonContent; + + var result = new CoreConfigV2rayService(context).GenerateClientConfigContent(); + + await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}"); + var generatedConfig = JsonNode.Parse(result.Data!.ToString()!)!; + var proxy = generatedConfig["outbounds"]!.AsArray() + .First(outbound => outbound!["tag"]!.GetValue() == Global.ProxyTag)!; + + await proxy["targetStrategy"]?.GetValue().Should().BeEqualTo("UseIPv4"); + } + + [Test] + public async Task GenerateClientConfigContent_CustomOutbound_ShouldKeepExplicitTargetStrategy() + { + var config = CoreConfigTestFactory.CreateConfig(ECoreType.Xray); + config.SimpleDNSItem.Strategy4Proxy = "UseIPv4"; + CoreConfigTestFactory.BindAppManagerConfig(config); + + var customNode = CoreConfigTestFactory.CreateCustomOutboundNode(ECoreType.Xray, "n-custom", "custom-xray"); + var customJsonContent = """ + { + "protocol": "shadowsocks", + "targetStrategy": "AsIs", + "settings": { + "servers": [ + { + "address": "1.2.3.4", + "port": 8388, + "method": "aes-128-gcm", + "password": "custom_password" + } + ] + } + } + """; + + var context = CoreConfigTestFactory.CreateContext(config, customNode, ECoreType.Xray); + context.CustomOutboundContent[customNode.IndexId] = customJsonContent; + + var result = new CoreConfigV2rayService(context).GenerateClientConfigContent(); + + await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}"); + var generatedConfig = JsonNode.Parse(result.Data!.ToString()!)!; + var proxy = generatedConfig["outbounds"]!.AsArray() + .First(outbound => outbound!["tag"]!.GetValue() == Global.ProxyTag)!; + + await proxy["targetStrategy"]?.GetValue().Should().BeEqualTo("AsIs"); + } + [Test] public async Task GenerateClientConfigContent_XdriveGoogleDrive_ShouldGenerateValidOutbound() { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index 9b45a697..d9114289 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -29,6 +29,7 @@ public partial class CoreConfigV2rayService continue; } var outboundTag = outbound.tag; + var outboundTargetStrategy = outbound.targetStrategy; var outboundDetour = outbound.streamSettings?.sockopt?.dialerProxy ?? string.Empty; var outboundBindInterface = outbound.streamSettings?.sockopt?.Interface ?? string.Empty; var customOutboundContent = context.CustomOutboundContent[customOutboundIndex]; @@ -44,6 +45,12 @@ public partial class CoreConfigV2rayService { customOutboundObj?["tag"] = outboundTag; } + if (customOutboundObj is not null + && customOutboundObj["targetStrategy"] is null + && !outboundTargetStrategy.IsNullOrEmpty()) + { + customOutboundObj["targetStrategy"] = outboundTargetStrategy; + } if (!containDetourPlaceholder && !outboundDetour.IsNullOrEmpty()) { customOutboundObj!["streamSettings"] ??= new JsonObject();