feat: add sing-box TUN dns_mode setting (#10336)

This commit is contained in:
Denis Elkhov
2026-11-07 09:44:37 +03:00
committed by GitHub
parent c01224c37d
commit 5211b9ce0e
21 changed files with 182 additions and 20 deletions
@@ -46,6 +46,72 @@ public class CoreConfigSingboxServiceTests
await cfg.inbounds.Should().Contain(i => i.type == "tun");
}
[Test]
[Arguments(null)]
[Arguments("Default")]
public async Task GenerateClientConfigContent_TunDnsModeDefault_ShouldNotIncludeDnsMode(string? dnsMode)
{
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.TunModeItem.EnableTun = true;
config.TunModeItem.DnsMode = dnsMode;
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateSocksNode(ECoreType.sing_box);
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
IsTunEnabled = true,
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
await result.Data!.ToString().Should().NotContain("\"dns_mode\"");
}
[Test]
public async Task GenerateClientConfigContent_TunDnsModeDisabled_ShouldIncludeDnsMode()
{
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.TunModeItem.EnableTun = true;
config.TunModeItem.DnsMode = "disabled";
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateSocksNode(ECoreType.sing_box);
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
IsTunEnabled = true,
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
var tun = cfg.inbounds.First(i => i.type == "tun");
await tun.dns_mode.Should().BeEqualTo("disabled");
}
[Test]
[Arguments(true)]
[Arguments(false)]
public async Task GenerateClientConfigContent_TunDnsModeDisabled_ShouldNotIncludeGlobalDnsHijackRule(bool sniffingEnabled)
{
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.TunModeItem.EnableTun = true;
config.TunModeItem.DnsMode = "disabled";
config.Inbound.First().SniffingEnabled = sniffingEnabled;
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateSocksNode(ECoreType.sing_box);
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
IsTunEnabled = true,
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
await cfg.route.rules.Any(
r => r.action == "hijack-dns" && r.process_path == null).Should().BeFalse();
}
[Test]
public async Task GenerateClientConfigContent_TunEnabled_ShouldKeepEmbeddedTunRules()
{
+10
View File
@@ -607,6 +607,16 @@ public class Global
"mixed"
];
public const string TunDnsModeDefault = "Default";
public static readonly List<string> TunDnsModes =
[
TunDnsModeDefault,
"disabled",
"native",
"hijack"
];
public static readonly List<string> PresetMsgFilters =
[
"proxy",
@@ -137,6 +137,7 @@ public class TunModeItem
public bool EnableIPv6Address { get; set; }
public string IcmpRouting { get; set; }
public bool EnableLegacyProtect { get; set; } = true;
public string? DnsMode { get; set; }
public List<string>? RouteExcludeAddress { get; set; }
public string IPv4Address { get; set; }
public string IPv6Address { get; set; }
@@ -114,6 +114,7 @@ public class Inbound4Sbox
public bool? auto_route { get; set; }
public bool? strict_route { get; set; }
public string? stack { get; set; }
public string? dns_mode { get; set; }
public List<User4Sbox> users { get; set; }
public List<string>? route_exclude_address { get; set; }
}
+9
View File
@@ -4913,6 +4913,15 @@ namespace ServiceLib.Resx {
return ResourceManager.GetString("TbSettingsTunAutoRoute", resourceCulture);
}
}
/// <summary>
/// 查找类似 DNS mode 的本地化字符串。
/// </summary>
public static string TbSettingsTunDnsMode {
get {
return ResourceManager.GetString("TbSettingsTunDnsMode", resourceCulture);
}
}
/// <summary>
/// 查找类似 Tun Mode settings 的本地化字符串。
+3
View File
@@ -1080,6 +1080,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Stek</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>DNS rejimi</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
+4 -1
View File
@@ -1065,6 +1065,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>پشته شبکه</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>حالت DNS</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1773,4 +1776,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuNewUpdate" xml:space="preserve">
<value>New Update</value>
</data>
</root>
</root>
+4 -1
View File
@@ -1059,6 +1059,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Pile de protocoles</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>Mode DNS</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1767,4 +1770,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuNewUpdate" xml:space="preserve">
<value>Nouvelle MAJ</value>
</data>
</root>
</root>
+4 -1
View File
@@ -1062,6 +1062,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Hálózati verem</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>DNS-mód</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1770,4 +1773,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuNewUpdate" xml:space="preserve">
<value>New Update</value>
</data>
</root>
</root>
+4 -1
View File
@@ -1062,6 +1062,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Stack</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>Mode DNS</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1794,4 +1797,4 @@ Tindakan "Ambil sertifikat" mungkin gagal jika sertifikat self-signed digunakan
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
<value>Memecah bagian akhir paket menjadi fragmen yang lebih kecil. Hal ini dapat memengaruhi throughput dan latensi.</value>
</data>
</root>
</root>
+3
View File
@@ -1083,6 +1083,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Stack</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>DNS mode</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
+4 -1
View File
@@ -1083,6 +1083,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>Сетевой стек</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>Режим DNS</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1905,4 +1908,4 @@
<data name="menuAddMasqueServer" xml:space="preserve">
<value>Добавить сервер [MASQUE]</value>
</data>
</root>
</root>
@@ -1071,6 +1071,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>协议栈</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>DNS 模式</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -1080,6 +1080,9 @@
<data name="TbSettingsTunStack" xml:space="preserve">
<value>協定堆疊</value>
</data>
<data name="TbSettingsTunDnsMode" xml:space="preserve">
<value>DNS 模式</value>
</data>
<data name="TbMtu" xml:space="preserve">
<value>MTU</value>
</data>
@@ -68,6 +68,9 @@ public partial class CoreConfigSingboxService
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack;
tunInbound.dns_mode = _config.TunModeItem.DnsMode == Global.TunDnsModeDefault
? null
: _config.TunModeItem.DnsMode.NullIfEmpty();
var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
tunInbound.address = [address];
@@ -113,23 +113,27 @@ public partial class CoreConfigSingboxService
}
}
var disableDnsHijack = string.Equals(_config.TunModeItem.DnsMode, "disabled", StringComparison.OrdinalIgnoreCase);
if (_config.Inbound.First().SniffingEnabled)
{
_coreConfig.route.rules.Add(new()
{
action = "sniff"
});
_coreConfig.route.rules.Add(new()
if (!disableDnsHijack)
{
type = "logical",
mode = "or",
action = "hijack-dns",
rules =
[
new() { port = [53] },
new() { protocol = ["dns"] },
],
});
_coreConfig.route.rules.Add(new()
{
type = "logical",
mode = "or",
action = "hijack-dns",
rules =
[
new() { port = [53] },
new() { protocol = ["dns"] },
],
});
}
if (_config.CoreBasicItem.EnableFinalFragment)
{
_coreConfig.route.rules.Add(new()
@@ -142,11 +146,14 @@ public partial class CoreConfigSingboxService
}
else
{
_coreConfig.route.rules.Add(new()
if (!disableDnsHijack)
{
port = [53],
action = "hijack-dns",
});
_coreConfig.route.rules.Add(new()
{
port = [53],
action = "hijack-dns",
});
}
if (_config.CoreBasicItem.EnableFinalFragment)
{
_coreConfig.route.rules.Add(new()
@@ -97,6 +97,7 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public partial bool TunEnableIPv6Address { get; set; }
[Reactive] public partial string TunIcmpRouting { get; set; }
[Reactive] public partial bool TunEnableLegacyProtect { get; set; }
[Reactive] public partial string TunDnsMode { get; set; }
[Reactive] public partial string TunRouteExcludeAddress { get; set; }
[Reactive] public partial string TunIPv4Address { get; set; }
[Reactive] public partial string TunIPv6Address { get; set; }
@@ -222,6 +223,7 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunIcmpRouting = _config.TunModeItem.IcmpRouting;
TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect;
TunDnsMode = _config.TunModeItem.DnsMode.NullIfEmpty() ?? Global.TunDnsModeDefault;
TunRouteExcludeAddress = Utils.List2String(_config.TunModeItem.RouteExcludeAddress, true);
TunIPv4Address = _config.TunModeItem.IPv4Address;
TunIPv6Address = _config.TunModeItem.IPv6Address;
@@ -395,6 +397,7 @@ public partial class OptionSettingViewModel : MyReactiveObject, ICloseable
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
_config.TunModeItem.IcmpRouting = TunIcmpRouting;
_config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect;
_config.TunModeItem.DnsMode = TunDnsMode == Global.TunDnsModeDefault ? null : TunDnsMode;
_config.TunModeItem.RouteExcludeAddress = Utils.String2List(TunRouteExcludeAddress);
_config.TunModeItem.IPv4Address = TunIPv4Address;
_config.TunModeItem.IPv6Address = TunIPv6Address;
@@ -1035,7 +1035,7 @@
Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto"
DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="2"
@@ -1189,6 +1189,20 @@
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="12"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsTunDnsMode}" />
<ComboBox
x:Name="cmbDnsMode"
Grid.Row="12"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -33,6 +33,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbmux4RayXudpProxyUDP443.ItemsSource = new[] { "reject", "skip" };
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbDnsMode.ItemsSource = Global.TunDnsModes;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
@@ -135,6 +136,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDnsMode, v => v.cmbDnsMode.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.SelectedValue).DisposeWith(disposables);
@@ -1278,6 +1278,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@@ -1458,6 +1459,22 @@
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="12"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsTunDnsMode}" />
<ComboBox
x:Name="cmbDnsMode"
Grid.Row="12"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -29,6 +29,7 @@ public partial class OptionSettingWindow
cmbmux4RayXudpProxyUDP443.ItemsSource = new[] { "reject", "skip" };
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbDnsMode.ItemsSource = Global.TunDnsModes;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
@@ -131,6 +132,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDnsMode, v => v.cmbDnsMode.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.Text).DisposeWith(disposables);