diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 3d918f99..4d05baff 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -12,6 +12,7 @@
+
@@ -26,7 +27,9 @@
+
+
-
\ No newline at end of file
+
diff --git a/v2rayN/ServiceLib.Tests/CoreConfigV2rayServiceTests.cs b/v2rayN/ServiceLib.Tests/CoreConfigV2rayServiceTests.cs
new file mode 100644
index 00000000..b085e5d9
--- /dev/null
+++ b/v2rayN/ServiceLib.Tests/CoreConfigV2rayServiceTests.cs
@@ -0,0 +1,228 @@
+using System.Text.Json.Nodes;
+using ServiceLib;
+using ServiceLib.Enums;
+using ServiceLib.Models;
+using ServiceLib.Services.CoreConfig;
+using Xunit;
+
+namespace ServiceLib.Tests;
+
+public class CoreConfigV2rayServiceTests
+{
+ private const string SendThrough = "198.51.100.10";
+
+ [Fact]
+ public void GenerateClientConfigContent_OnlyAppliesSendThroughToRemoteProxyOutbounds()
+ {
+ var node = CreateProxyNode("proxy-1", "198.51.100.1", 443);
+ var service = new CoreConfigV2rayService(CreateContext(node));
+
+ var result = service.GenerateClientConfigContent();
+
+ Assert.True(result.Success);
+
+ var outbounds = GetOutbounds(result.Data?.ToString());
+ var proxyOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue() == Global.ProxyTag);
+ var directOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue() == Global.DirectTag);
+ var blockOutbound = outbounds.Single(outbound => outbound["tag"]!.GetValue() == Global.BlockTag);
+
+ Assert.Equal(SendThrough, proxyOutbound["sendThrough"]?.GetValue());
+ Assert.Null(directOutbound["sendThrough"]);
+ Assert.Null(blockOutbound["sendThrough"]);
+ }
+
+ [Fact]
+ public void GenerateClientConfigContent_OnlyAppliesSendThroughToChainExitOutbounds()
+ {
+ var exitNode = CreateProxyNode("exit", "198.51.100.2", 443);
+ var entryNode = CreateProxyNode("entry", "198.51.100.3", 443);
+ var chainNode = CreateChainNode("chain", exitNode, entryNode);
+
+ var service = new CoreConfigV2rayService(CreateContext(
+ chainNode,
+ allProxiesMap: new Dictionary
+ {
+ [exitNode.IndexId] = exitNode,
+ [entryNode.IndexId] = entryNode,
+ }));
+
+ var result = service.GenerateClientConfigContent();
+
+ Assert.True(result.Success);
+
+ var outbounds = GetOutbounds(result.Data?.ToString())
+ .Where(outbound => outbound["protocol"]?.GetValue() is not ("freedom" or "blackhole" or "dns"))
+ .ToList();
+
+ var sendThroughOutbounds = outbounds
+ .Where(outbound => outbound["sendThrough"]?.GetValue() == SendThrough)
+ .ToList();
+ var chainedOutbounds = outbounds
+ .Where(outbound => outbound["streamSettings"]?["sockopt"]?["dialerProxy"] is not null)
+ .ToList();
+
+ Assert.Single(sendThroughOutbounds);
+ Assert.All(chainedOutbounds, outbound => Assert.Null(outbound["sendThrough"]));
+ }
+
+ [Fact]
+ public void GenerateClientConfigContent_DoesNotApplySendThroughToTunRelayLoopbackOutbound()
+ {
+ var node = CreateProxyNode("proxy-1", "198.51.100.4", 443);
+ var config = CreateConfig();
+ config.TunModeItem.EnableLegacyProtect = false;
+
+ var service = new CoreConfigV2rayService(CreateContext(
+ node,
+ config,
+ isTunEnabled: true,
+ tunProtectSsPort: 10811,
+ proxyRelaySsPort: 10812));
+
+ var result = service.GenerateClientConfigContent();
+
+ Assert.True(result.Success);
+
+ var outbounds = GetOutbounds(result.Data?.ToString());
+ Assert.DoesNotContain(outbounds, outbound => outbound["sendThrough"]?.GetValue() == SendThrough);
+ }
+
+ private static CoreConfigContext CreateContext(
+ ProfileItem node,
+ Config? config = null,
+ Dictionary? allProxiesMap = null,
+ bool isTunEnabled = false,
+ int tunProtectSsPort = 0,
+ int proxyRelaySsPort = 0)
+ {
+ return new CoreConfigContext
+ {
+ Node = node,
+ RunCoreType = ECoreType.Xray,
+ AppConfig = config ?? CreateConfig(),
+ AllProxiesMap = allProxiesMap ?? new(),
+ SimpleDnsItem = new SimpleDNSItem(),
+ IsTunEnabled = isTunEnabled,
+ TunProtectSsPort = tunProtectSsPort,
+ ProxyRelaySsPort = proxyRelaySsPort,
+ };
+ }
+
+ private static Config CreateConfig()
+ {
+ return new Config
+ {
+ IndexId = string.Empty,
+ SubIndexId = string.Empty,
+ CoreBasicItem = new()
+ {
+ LogEnabled = false,
+ Loglevel = "warning",
+ MuxEnabled = false,
+ DefAllowInsecure = false,
+ DefFingerprint = Global.Fingerprints.First(),
+ DefUserAgent = string.Empty,
+ SendThrough = SendThrough,
+ EnableFragment = false,
+ EnableCacheFile4Sbox = true,
+ },
+ TunModeItem = new()
+ {
+ EnableTun = false,
+ AutoRoute = true,
+ StrictRoute = true,
+ Stack = string.Empty,
+ Mtu = 9000,
+ EnableIPv6Address = false,
+ IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
+ EnableLegacyProtect = false,
+ },
+ KcpItem = new(),
+ GrpcItem = new(),
+ RoutingBasicItem = new()
+ {
+ DomainStrategy = Global.DomainStrategies.First(),
+ DomainStrategy4Singbox = Global.DomainStrategies4Sbox.First(),
+ RoutingIndexId = string.Empty,
+ },
+ GuiItem = new(),
+ MsgUIItem = new(),
+ UiItem = new()
+ {
+ CurrentLanguage = "en",
+ CurrentFontFamily = string.Empty,
+ MainColumnItem = [],
+ WindowSizeItem = [],
+ },
+ ConstItem = new(),
+ SpeedTestItem = new(),
+ Mux4RayItem = new()
+ {
+ Concurrency = 8,
+ XudpConcurrency = 8,
+ XudpProxyUDP443 = "reject",
+ },
+ Mux4SboxItem = new()
+ {
+ Protocol = string.Empty,
+ },
+ HysteriaItem = new(),
+ ClashUIItem = new()
+ {
+ ConnectionsColumnItem = [],
+ },
+ SystemProxyItem = new(),
+ WebDavItem = new(),
+ CheckUpdateItem = new(),
+ Fragment4RayItem = null,
+ Inbound = [new InItem
+ {
+ Protocol = EInboundProtocol.socks.ToString(),
+ LocalPort = 10808,
+ UdpEnabled = true,
+ SniffingEnabled = true,
+ RouteOnly = false,
+ }],
+ GlobalHotkeys = [],
+ CoreTypeItem = [],
+ SimpleDNSItem = new(),
+ };
+ }
+
+ private static ProfileItem CreateProxyNode(string indexId, string address, int port)
+ {
+ return new ProfileItem
+ {
+ IndexId = indexId,
+ Remarks = indexId,
+ ConfigType = EConfigType.SOCKS,
+ CoreType = ECoreType.Xray,
+ Address = address,
+ Port = port,
+ };
+ }
+
+ private static ProfileItem CreateChainNode(string indexId, params ProfileItem[] nodes)
+ {
+ var chainNode = new ProfileItem
+ {
+ IndexId = indexId,
+ Remarks = indexId,
+ ConfigType = EConfigType.ProxyChain,
+ CoreType = ECoreType.Xray,
+ };
+ chainNode.SetProtocolExtra(new ProtocolExtraItem
+ {
+ ChildItems = string.Join(',', nodes.Select(node => node.IndexId)),
+ });
+ return chainNode;
+ }
+
+ private static List GetOutbounds(string? json)
+ {
+ var root = JsonNode.Parse(json ?? throw new InvalidOperationException("Config JSON is missing"))?.AsObject()
+ ?? throw new InvalidOperationException("Failed to parse config JSON");
+ return root["outbounds"]?.AsArray().Select(node => node!.AsObject()).ToList()
+ ?? throw new InvalidOperationException("Config JSON does not contain outbounds");
+ }
+}
diff --git a/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj b/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj
new file mode 100644
index 00000000..4e1ed1a7
--- /dev/null
+++ b/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ false
+ true
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index 3907b1a7..c0d8816d 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -522,6 +522,23 @@ public class Utils
return false;
}
+ public static bool IsIpv4(string? ip)
+ {
+ if (ip.IsNullOrEmpty())
+ {
+ return false;
+ }
+
+ ip = ip.Trim();
+ if (!IPAddress.TryParse(ip, out var address))
+ {
+ return false;
+ }
+
+ return address.AddressFamily == AddressFamily.InterNetwork
+ && ip.Count(c => c == '.') == 3;
+ }
+
public static bool IsIpAddress(string? ip)
{
if (ip.IsNullOrEmpty())
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 167820f0..78229e6c 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -41,6 +41,7 @@ public static class ConfigHandler
Loglevel = "warning",
MuxEnabled = false,
};
+ config.CoreBasicItem.SendThrough = config.CoreBasicItem.SendThrough?.TrimEx();
if (config.Inbound == null)
{
diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs
index 0f0e257c..fa766f0d 100644
--- a/v2rayN/ServiceLib/Models/ConfigItems.cs
+++ b/v2rayN/ServiceLib/Models/ConfigItems.cs
@@ -15,6 +15,8 @@ public class CoreBasicItem
public string DefUserAgent { get; set; }
+ public string? SendThrough { get; set; }
+
public bool EnableFragment { get; set; }
public bool EnableCacheFile4Sbox { get; set; } = true;
diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs
index 983cb4da..1bde2dc2 100644
--- a/v2rayN/ServiceLib/Models/V2rayConfig.cs
+++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs
@@ -105,6 +105,8 @@ public class Outbounds4Ray
public string protocol { get; set; }
+ public string? sendThrough { get; set; }
+
public string? targetStrategy { get; set; }
public Outboundsettings4Ray settings { get; set; }
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 4498d999..81b46ac6 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -222,6 +222,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Please fill in the correct IPv4 address for SendThrough. 的本地化字符串。
+ ///
+ public static string FillCorrectSendThroughIPv4 {
+ get {
+ return ResourceManager.GetString("FillCorrectSendThroughIPv4", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Please enter the correct port format. 的本地化字符串。
///
@@ -4005,6 +4014,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Local outbound address (SendThrough) 的本地化字符串。
+ ///
+ public static string TbSettingsSendThrough {
+ get {
+ return ResourceManager.GetString("TbSettingsSendThrough", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Only applies to Xray. Fill in a local IPv4 address; leave empty to disable. 的本地化字符串。
+ ///
+ public static string TbSettingsSendThroughTip {
+ get {
+ return ResourceManager.GetString("TbSettingsSendThroughTip", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Enable Log 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 569877d7..fbab6252 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1320,6 +1320,15 @@
The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.
+
+ Local outbound address (SendThrough)
+
+
+ Only applies to Xray. Fill in a local IPv4 address; leave empty to disable.
+
+
+ Please fill in the correct IPv4 address for SendThrough.
+
*xhttp mode
@@ -1698,4 +1707,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Legacy TUN Protect
-
\ No newline at end of file
+
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index 8c628644..8416287e 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1317,6 +1317,15 @@
密码将调用命令行校验,如果因为校验错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。
+
+ 本地出站地址 (SendThrough)
+
+
+ 仅对 Xray 生效,填写本机 IPv4;留空则不设置。
+
+
+ 请填写正确的 SendThrough IPv4 地址。
+
*XHTTP 模式
@@ -1695,4 +1704,4 @@
旧版 TUN 保护
-
\ No newline at end of file
+
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
index 2c544aa5..1e72de41 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
@@ -62,6 +62,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
GenDns();
GenStatistic();
+ ApplyOutboundSendThrough();
var finalRule = BuildFinalRule();
if (!string.IsNullOrEmpty(finalRule?.balancerTag))
@@ -195,6 +196,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
_coreConfig.routing.rules.Add(rule);
}
+ ApplyOutboundSendThrough();
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
ret.Success = true;
ret.Data = JsonUtils.Serialize(_coreConfig);
@@ -255,6 +257,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
});
_coreConfig.routing.rules.Add(BuildFinalRule());
+ ApplyOutboundSendThrough();
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
@@ -375,6 +378,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
//_coreConfig.inbounds.Clear();
+ ApplyOutboundSendThrough();
var configNode = JsonUtils.ParseJson(JsonUtils.Serialize(_coreConfig))!;
configNode["inbounds"]!.AsArray().Add(new
{
@@ -403,4 +407,43 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
}
#endregion public gen function
+
+ private void ApplyOutboundSendThrough()
+ {
+ var sendThrough = _config.CoreBasicItem.SendThrough?.TrimEx();
+ foreach (var outbound in _coreConfig.outbounds ?? [])
+ {
+ outbound.sendThrough = ShouldApplySendThrough(outbound, sendThrough) ? sendThrough : null;
+ }
+ }
+
+ private static bool ShouldApplySendThrough(Outbounds4Ray outbound, string? sendThrough)
+ {
+ if (sendThrough.IsNullOrEmpty())
+ {
+ return false;
+ }
+
+ if (outbound.protocol is "freedom" or "blackhole" or "dns" or "loopback")
+ {
+ return false;
+ }
+
+ if (outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == false)
+ {
+ return false;
+ }
+
+ var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
+ ?? outbound.settings?.vnext?.FirstOrDefault()?.address
+ ?? outbound.settings?.address?.ToString()
+ ?? string.Empty;
+
+ if (outboundAddress.Equals("localhost", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return !IPAddress.TryParse(outboundAddress, out var address) || !IPAddress.IsLoopback(address);
+ }
}
diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
index 8b677ffd..1d26f02f 100644
--- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
@@ -20,6 +20,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public bool defAllowInsecure { get; set; }
[Reactive] public string defFingerprint { get; set; }
[Reactive] public string defUserAgent { get; set; }
+ [Reactive] public string sendThrough { get; set; }
[Reactive] public string mux4SboxProtocol { get; set; }
[Reactive] public bool enableCacheFile4Sbox { get; set; }
[Reactive] public int? hyUpMbps { get; set; }
@@ -154,6 +155,7 @@ public class OptionSettingViewModel : MyReactiveObject
defAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
defFingerprint = _config.CoreBasicItem.DefFingerprint;
defUserAgent = _config.CoreBasicItem.DefUserAgent;
+ sendThrough = _config.CoreBasicItem.SendThrough;
mux4SboxProtocol = _config.Mux4SboxItem.Protocol;
enableCacheFile4Sbox = _config.CoreBasicItem.EnableCacheFile4Sbox;
hyUpMbps = _config.HysteriaItem.UpMbps;
@@ -297,6 +299,12 @@ public class OptionSettingViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
return;
}
+ var sendThroughValue = sendThrough?.TrimEx();
+ if (sendThroughValue.IsNotEmpty() && !Utils.IsIpv4(sendThroughValue))
+ {
+ NoticeManager.Instance.Enqueue(ResUI.FillCorrectSendThroughIPv4);
+ return;
+ }
var needReboot = EnableStatistics != _config.GuiItem.EnableStatistics
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
@@ -336,6 +344,7 @@ public class OptionSettingViewModel : MyReactiveObject
_config.CoreBasicItem.DefAllowInsecure = defAllowInsecure;
_config.CoreBasicItem.DefFingerprint = defFingerprint;
_config.CoreBasicItem.DefUserAgent = defUserAgent;
+ _config.CoreBasicItem.SendThrough = sendThrough?.TrimEx();
_config.Mux4SboxItem.Protocol = mux4SboxProtocol;
_config.CoreBasicItem.EnableCacheFile4Sbox = enableCacheFile4Sbox;
_config.HysteriaItem.UpMbps = hyUpMbps ?? 0;
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
index 78483180..640a4f6f 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
@@ -325,6 +325,27 @@
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
+
+
+
+
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
index 2bfe6b4c..26e55646 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
@@ -76,6 +76,7 @@ public partial class OptionSettingWindow : WindowBase
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.sendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
diff --git a/v2rayN/v2rayN.sln b/v2rayN/v2rayN.sln
index 4e9ee76e..adf2cbf1 100644
--- a/v2rayN/v2rayN.sln
+++ b/v2rayN/v2rayN.sln
@@ -32,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Action", "GitHub Act
..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLib.Tests", "ServiceLib.Tests\ServiceLib.Tests.csproj", "{E0B6C5C7-ED48-42EB-947A-877779E9F555}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,6 +60,10 @@ Global
{CB3DE54F-3A26-AE02-1299-311132C32156}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB3DE54F-3A26-AE02-1299-311132C32156}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E0B6C5C7-ED48-42EB-947A-877779E9F555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E0B6C5C7-ED48-42EB-947A-877779E9F555}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E0B6C5C7-ED48-42EB-947A-877779E9F555}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E0B6C5C7-ED48-42EB-947A-877779E9F555}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
index a3fc391e..5a545c65 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
@@ -391,6 +391,30 @@
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
+
+
+
+
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
index 80560607..2af77833 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
@@ -73,6 +73,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.Text).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.sendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);