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();