diff --git a/Netch/Controllers/DNSController.cs b/Netch/Controllers/DNSController.cs index 2e11f682..d98a593f 100644 --- a/Netch/Controllers/DNSController.cs +++ b/Netch/Controllers/DNSController.cs @@ -1,7 +1,7 @@ -using System; -using System.IO; +using System.IO; using System.Threading.Tasks; using Netch.Interfaces; +using Netch.Models; using static Netch.Interops.AioDNS; namespace Netch.Controllers @@ -10,26 +10,24 @@ namespace Netch.Controllers { public string Name => "DNS Service"; - public async Task StopAsync() + public async Task StartAsync(string listenAddress) { - await FreeAsync(); - } - - public async Task StartAsync() - { - MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS"); - var aioDnsConfig = Global.Settings.AioDNS; - var listenAddress = Global.Settings.LocalAddress; Dial(NameList.TYPE_REST, ""); Dial(NameList.TYPE_LIST, Path.GetFullPath(Constants.AioDnsRuleFile)); + // TODO remove ListenPort setting Dial(NameList.TYPE_LISN, $"{listenAddress}:{aioDnsConfig.ListenPort}"); Dial(NameList.TYPE_CDNS, $"{aioDnsConfig.ChinaDNS}"); Dial(NameList.TYPE_ODNS, $"{aioDnsConfig.OtherDNS}"); if (!await InitAsync()) - throw new Exception("AioDNS start failed."); + throw new MessageException("AioDNS start failed."); + } + + public async Task StopAsync() + { + await FreeAsync(); } } } \ No newline at end of file diff --git a/Netch/Controllers/MainController.cs b/Netch/Controllers/MainController.cs index b049f24c..e904bf8d 100644 --- a/Netch/Controllers/MainController.cs +++ b/Netch/Controllers/MainController.cs @@ -102,7 +102,11 @@ namespace Netch.Controllers if (Lock.CurrentCount == 0) { (await Lock.EnterAsync()).Dispose(); - return; + if (ServerController == null && ModeController == null) + // stopped + return; + + // else begin stop } using var _ = await Lock.EnterAsync(); diff --git a/Netch/Controllers/NFController.cs b/Netch/Controllers/NFController.cs index c761d0b6..82a02e6c 100644 --- a/Netch/Controllers/NFController.cs +++ b/Netch/Controllers/NFController.cs @@ -59,7 +59,7 @@ namespace Netch.Controllers { var dnsStr = _mode.FilterDNS != null ? _mode.DNSHost : _rdrConfig.DNSHost; - dnsStr = dnsStr.ValueOrDefault() ?? Constants.DefaultPrimaryDNS; + dnsStr = dnsStr.ValueOrDefault() ?? $"{Constants.DefaultPrimaryDNS}:53"; var dns = IPEndPoint.Parse(dnsStr); if (dns.Port == 0) diff --git a/Netch/Controllers/TUNController.cs b/Netch/Controllers/TUNController.cs index 450b678a..4d328a09 100644 --- a/Netch/Controllers/TUNController.cs +++ b/Netch/Controllers/TUNController.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading.Tasks; using Netch.Interfaces; @@ -11,27 +13,34 @@ using Netch.Models.Modes.TunMode; using Netch.Servers; using Netch.Utils; using Serilog; -using static Netch.Interops.tun2socks; namespace Netch.Controllers { - public class TUNController : IModeController + public class TUNController : Guard, IModeController { - private const string DummyDns = "6.6.6.6"; - private readonly DNSController _aioDnsController = new(); private TunMode _mode = null!; private IPAddress? _serverRemoteAddress; private TUNConfig _tunConfig = null!; + private bool _routeSetuped = false; private NetRoute _tun; + private NetworkInterface _tunNetworkInterface = null!; private NetRoute _outbound; - public string Name => "tun2socks"; + public override string Name => "tun2socks"; public ModeFeature Features => ModeFeature.SupportSocks5Auth; + protected override IEnumerable StartedKeywords { get; } = new[] { "Creating adapter" }; + + protected override IEnumerable FailedKeywords { get; } = new[] { "panic" }; + + public TUNController() : base("tun2socks.exe") + { + } + public async Task StartAsync(Socks5Server server, Mode mode) { if (mode is not TunMode tunMode) @@ -51,51 +60,40 @@ namespace Netch.Controllers _outbound = NetRoute.GetBestRouteTemplate(); CheckDriver(); - Dial(NameList.TYPE_ADAPMTU, "1500"); - Dial(NameList.TYPE_BYPBIND, _outbound.Gateway); - Dial(NameList.TYPE_BYPLIST, "disabled"); + var proxy = server.Auth() + ? $"socks5://{server.Username}:{server.Password}@{await server.AutoResolveHostnameAsync()}:{server.Port}" + : $"socks5://{await server.AutoResolveHostnameAsync()}:{server.Port}"; - #region Server - - Dial(NameList.TYPE_TCPREST, ""); - Dial(NameList.TYPE_TCPTYPE, "Socks5"); - - Dial(NameList.TYPE_UDPREST, ""); - Dial(NameList.TYPE_UDPTYPE, "Socks5"); - - Dial(NameList.TYPE_TCPHOST, $"{await server.AutoResolveHostnameAsync()}:{server.Port}"); - - Dial(NameList.TYPE_UDPHOST, $"{await server.AutoResolveHostnameAsync()}:{server.Port}"); - - if (server.Auth()) + const string interfaceName = "netch"; + var arguments = new object?[] { - Dial(NameList.TYPE_TCPUSER, server.Username!); - Dial(NameList.TYPE_TCPPASS, server.Password!); + // -device tun://aioCloud -proxy socks5://127.0.0.1:7890 + "-device", $"tun://{interfaceName}", + "-proxy", proxy, + "-mtu", "1500" + }; - Dial(NameList.TYPE_UDPUSER, server.Username!); - Dial(NameList.TYPE_UDPPASS, server.Password!); + await StartGuardAsync(Arguments.Format(arguments)); + + // Wait for adapter to be created + for (var i = 0; i < 20; i++) + { + await Task.Delay(300); + try + { + _tunNetworkInterface = NetworkInterfaceUtils.Get(ni => ni.Name.StartsWith(interfaceName)); + break; + } + catch + { + // ignored + } } - #endregion + if (_tunNetworkInterface == null) + throw new MessageException("Create wintun adapter failed"); - #region DNS - - if (_tunConfig.UseCustomDNS) - { - Dial(NameList.TYPE_DNSADDR, _tunConfig.HijackDNS); - } - else - { - await _aioDnsController.StartAsync(); - Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}"); - } - - #endregion - - if (!Init()) - throw new MessageException("tun2socks start failed."); - - var tunIndex = (int)RouteHelper.ConvertLuidToIndex(tun_luid()); + var tunIndex = _tunNetworkInterface.GetIndex(); _tun = NetRoute.TemplateBuilder(_tunConfig.Gateway, tunIndex); RouteHelper.CreateUnicastIP(AddressFamily.InterNetwork, @@ -103,14 +101,14 @@ namespace Netch.Controllers (byte)Utils.Utils.SubnetToCidr(_tunConfig.Netmask), (ulong)tunIndex); - SetupRouteTable(); + await SetupRouteTableAsync(); } - public async Task StopAsync() + public override async Task StopAsync() { var tasks = new[] { - FreeAsync(), + StopGuardAsync(), Task.Run(ClearRouteTable), _aioDnsController.StopAsync() }; @@ -120,35 +118,33 @@ namespace Netch.Controllers private void CheckDriver() { - string binDriver = Path.Combine(Global.NetchDir, Constants.WintunDllFile); - string sysDriver = $@"{Environment.SystemDirectory}\wintun.dll"; - - var binHash = Utils.Utils.SHA256CheckSum(binDriver); - var sysHash = Utils.Utils.SHA256CheckSum(sysDriver); - Log.Information("Built-in wintun.dll Hash: {Hash}", binHash); - Log.Information("Installed wintun.dll Hash: {Hash}", sysHash); - if (binHash == sysHash) - return; - + var f = $@"{Environment.SystemDirectory}\wintun.dll"; try { - Log.Information("Copy wintun.dll to System Directory"); - File.Copy(binDriver, sysDriver, true); + if (File.Exists(f)) + { + Log.Information($"Remove unused \"{f}\""); + File.Delete(f); + } } - catch (Exception e) + catch { - Log.Error(e, "Copy wintun.dll failed"); - throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}"); + // ignored } } #region Route - private void SetupRouteTable() + private async Task SetupRouteTableAsync() { + // _outbound: not go through proxy + // _tun: tun -> socks5 + // aiodns: a simple dns server with dns routing + + + _routeSetuped = true; Global.MainForm.StatusText(i18N.Translate("Setup Route Table Rule")); - var tunNetworkInterface = NetworkInterfaceUtils.Get(_tun.InterfaceIndex); // Server Address if (_serverRemoteAddress != null) RouteUtils.CreateRoute(_outbound.FillTemplate(_serverRemoteAddress.ToString(), 32)); @@ -161,21 +157,33 @@ namespace Netch.Controllers RouteUtils.CreateRouteFill(_outbound, _mode.Bypass); // dns - // NOTICE: DNS metric is network interface metric - tunNetworkInterface.SetDns(DummyDns); - RouteUtils.CreateRoute(_tun.FillTemplate(DummyDns, 32)); - - if (!_tunConfig.UseCustomDNS) + if (_tunConfig.UseCustomDNS) { + if (_tunConfig.ProxyDNS) + RouteUtils.CreateRoute(_tun.FillTemplate(_tunConfig.DNS, 32)); + + _tunNetworkInterface.SetDns(_tunConfig.DNS); + } + else + { + // aiodns RouteUtils.CreateRoute(_outbound.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.ChinaDNS), 32)); RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.OtherDNS), 32)); + // aiodns listen on tun interface + await _aioDnsController.StartAsync(Global.Settings.TUNTAP.Address); + + _tunNetworkInterface.SetDns(_tunConfig.Address); } + // set tun interface's metric to the highest to let Windows use the interface's DNS NetworkInterfaceUtils.SetInterfaceMetric(_tun.InterfaceIndex, 0); } private void ClearRouteTable() { + if (!_routeSetuped) + return; + if (_serverRemoteAddress != null) RouteUtils.DeleteRoute(_outbound.FillTemplate(_serverRemoteAddress.ToString(), 32)); diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs index 566c2143..7afa52bb 100644 --- a/Netch/Forms/MainForm.cs +++ b/Netch/Forms/MainForm.cs @@ -548,7 +548,11 @@ namespace Netch.Forms try { - await MainController.StartAsync(server, mode); + // "async all the way" + // https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#async-all-the-way + + // here is a dirty hack because the MainController don't need to switch back to UI thread, let it run in threadpool + await Task.Run(async () => await MainController.StartAsync(server, mode)); } catch (Exception exception) { diff --git a/Netch/Forms/ModeForms/ProcessForm.cs b/Netch/Forms/ModeForms/ProcessForm.cs index 92324740..e1abff81 100644 --- a/Netch/Forms/ModeForms/ProcessForm.cs +++ b/Netch/Forms/ModeForms/ProcessForm.cs @@ -49,7 +49,7 @@ namespace Netch.Forms.ModeForms BindSyncGlobalCheckBox(HandleTCPCheckBox, b => _mode.FilterTCP = b, _mode.FilterTCP, g.FilterTCP); BindSyncGlobalCheckBox(HandleUDPCheckBox, b => _mode.FilterUDP = b, _mode.FilterUDP, g.FilterUDP); BindSyncGlobalCheckBox(HandleDNSCheckBox, b => _mode.FilterDNS = b, _mode.FilterDNS, g.FilterDNS); - BindTextBox(DNSTextBox, s => IPEndPoint.TryParse(s, out _), s => _mode.DNSHost = s, _mode.DNSHost ?? Constants.DefaultPrimaryDNS); + BindTextBox(DNSTextBox, s => IPEndPoint.TryParse(s, out _), s => _mode.DNSHost = s, _mode.DNSHost ?? $"{Constants.DefaultPrimaryDNS}:53"); BindSyncGlobalCheckBox(HandleProcDNSCheckBox, b => _mode.HandleOnlyDNS = b, _mode.HandleOnlyDNS, g.HandleOnlyDNS); BindSyncGlobalCheckBox(ProxyDNSCheckBox, b => _mode.DNSProxy = b, _mode.DNSProxy, g.DNSProxy); diff --git a/Netch/Forms/ModeForms/RouteForm.cs b/Netch/Forms/ModeForms/RouteForm.cs index 70cb6b8b..7909c516 100644 --- a/Netch/Forms/ModeForms/RouteForm.cs +++ b/Netch/Forms/ModeForms/RouteForm.cs @@ -67,6 +67,8 @@ namespace Netch.Forms.ModeForms return; } + SaveBinds(); + if (IsCreateMode) { var relativePath = FilenameTextBox.Text; @@ -86,8 +88,10 @@ namespace Netch.Forms.ModeForms { _mode.WriteFile(); MessageBoxX.Show(i18N.Translate("Mode updated successfully")); + ModeService.Instance.Sort(); } + Global.MainForm.ModeComboBox.SelectedItem = _mode; Close(); } diff --git a/Netch/Forms/SettingForm.Designer.cs b/Netch/Forms/SettingForm.Designer.cs index 8e6c77c5..7fd82605 100644 --- a/Netch/Forms/SettingForm.Designer.cs +++ b/Netch/Forms/SettingForm.Designer.cs @@ -59,8 +59,8 @@ namespace Netch.Forms this.ICMPDelayTextBox = new System.Windows.Forms.TextBox(); this.FilterDNSCheckBox = new System.Windows.Forms.CheckBox(); this.DNSHijackHostTextBox = new System.Windows.Forms.TextBox(); - this.DNSProxyCheckBox = new System.Windows.Forms.CheckBox(); this.HandleProcDNSCheckBox = new System.Windows.Forms.CheckBox(); + this.DNSProxyCheckBox = new System.Windows.Forms.CheckBox(); this.ChildProcessHandleCheckBox = new System.Windows.Forms.CheckBox(); this.WinTUNTabPage = new System.Windows.Forms.TabPage(); this.WinTUNGroupBox = new System.Windows.Forms.GroupBox(); @@ -347,7 +347,7 @@ namespace Netch.Forms this.FilterTCPCheckBox.AutoSize = true; this.FilterTCPCheckBox.Location = new System.Drawing.Point(16, 16); this.FilterTCPCheckBox.Name = "FilterTCPCheckBox"; - this.FilterTCPCheckBox.Size = new System.Drawing.Size(81, 21); + this.FilterTCPCheckBox.Size = new System.Drawing.Size(94, 21); this.FilterTCPCheckBox.TabIndex = 1; this.FilterTCPCheckBox.Text = "Handle TCP"; this.FilterTCPCheckBox.UseVisualStyleBackColor = true; @@ -357,7 +357,7 @@ namespace Netch.Forms this.FilterUDPCheckBox.AutoSize = true; this.FilterUDPCheckBox.Location = new System.Drawing.Point(216, 16); this.FilterUDPCheckBox.Name = "FilterUDPCheckBox"; - this.FilterUDPCheckBox.Size = new System.Drawing.Size(84, 21); + this.FilterUDPCheckBox.Size = new System.Drawing.Size(97, 21); this.FilterUDPCheckBox.TabIndex = 2; this.FilterUDPCheckBox.Text = "Handle UDP"; this.FilterUDPCheckBox.UseVisualStyleBackColor = true; @@ -367,7 +367,7 @@ namespace Netch.Forms this.FilterICMPCheckBox.AutoSize = true; this.FilterICMPCheckBox.Location = new System.Drawing.Point(16, 48); this.FilterICMPCheckBox.Name = "FilterICMPCheckBox"; - this.FilterICMPCheckBox.Size = new System.Drawing.Size(90, 21); + this.FilterICMPCheckBox.Size = new System.Drawing.Size(103, 21); this.FilterICMPCheckBox.TabIndex = 3; this.FilterICMPCheckBox.Text = "Handle ICMP"; this.FilterICMPCheckBox.UseVisualStyleBackColor = true; @@ -386,7 +386,7 @@ namespace Netch.Forms this.ICMPDelayLabel.AutoSize = true; this.ICMPDelayLabel.Location = new System.Drawing.Point(48, 80); this.ICMPDelayLabel.Name = "ICMPDelayLabel"; - this.ICMPDelayLabel.Size = new System.Drawing.Size(100, 17); + this.ICMPDelayLabel.Size = new System.Drawing.Size(99, 17); this.ICMPDelayLabel.TabIndex = 3; this.ICMPDelayLabel.Text = "ICMP delay(ms)"; // @@ -404,7 +404,7 @@ namespace Netch.Forms this.FilterDNSCheckBox.AutoSize = true; this.FilterDNSCheckBox.Location = new System.Drawing.Point(16, 112); this.FilterDNSCheckBox.Name = "FilterDNSCheckBox"; - this.FilterDNSCheckBox.Size = new System.Drawing.Size(85, 21); + this.FilterDNSCheckBox.Size = new System.Drawing.Size(191, 21); this.FilterDNSCheckBox.TabIndex = 5; this.FilterDNSCheckBox.Text = "Handle DNS (DNS hijacking)"; this.FilterDNSCheckBox.UseVisualStyleBackColor = true; @@ -418,34 +418,34 @@ namespace Netch.Forms this.DNSHijackHostTextBox.TabIndex = 6; this.DNSHijackHostTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // - // DNSProxyCheckBox - // - this.DNSProxyCheckBox.AutoSize = true; - this.DNSProxyCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.FilterDNSCheckBox, "Checked", true)); - this.DNSProxyCheckBox.Location = new System.Drawing.Point(16, 208); - this.DNSProxyCheckBox.Name = "DNSProxyCheckBox"; - this.DNSProxyCheckBox.Size = new System.Drawing.Size(184, 21); - this.DNSProxyCheckBox.TabIndex = 8; - this.DNSProxyCheckBox.Text = "Handle DNS through proxy"; - this.DNSProxyCheckBox.UseVisualStyleBackColor = true; - // // HandleProcDNSCheckBox // this.HandleProcDNSCheckBox.AutoSize = true; this.HandleProcDNSCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.FilterDNSCheckBox, "Checked", true)); this.HandleProcDNSCheckBox.Location = new System.Drawing.Point(16, 176); this.HandleProcDNSCheckBox.Name = "HandleProcDNSCheckBox"; - this.HandleProcDNSCheckBox.Size = new System.Drawing.Size(203, 21); + this.HandleProcDNSCheckBox.Size = new System.Drawing.Size(208, 21); this.HandleProcDNSCheckBox.TabIndex = 7; - this.HandleProcDNSCheckBox.Text = "Handle handled process's DNS"; + this.HandleProcDNSCheckBox.Text = "Handle handled process\'s DNS"; this.HandleProcDNSCheckBox.UseVisualStyleBackColor = true; // + // DNSProxyCheckBox + // + this.DNSProxyCheckBox.AutoSize = true; + this.DNSProxyCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.FilterDNSCheckBox, "Checked", true)); + this.DNSProxyCheckBox.Location = new System.Drawing.Point(16, 208); + this.DNSProxyCheckBox.Name = "DNSProxyCheckBox"; + this.DNSProxyCheckBox.Size = new System.Drawing.Size(185, 21); + this.DNSProxyCheckBox.TabIndex = 8; + this.DNSProxyCheckBox.Text = "Handle DNS through proxy"; + this.DNSProxyCheckBox.UseVisualStyleBackColor = true; + // // ChildProcessHandleCheckBox // this.ChildProcessHandleCheckBox.AutoSize = true; this.ChildProcessHandleCheckBox.Location = new System.Drawing.Point(16, 240); this.ChildProcessHandleCheckBox.Name = "ChildProcessHandleCheckBox"; - this.ChildProcessHandleCheckBox.Size = new System.Drawing.Size(150, 21); + this.ChildProcessHandleCheckBox.Size = new System.Drawing.Size(149, 21); this.ChildProcessHandleCheckBox.TabIndex = 9; this.ChildProcessHandleCheckBox.Text = "Handle child process"; this.ChildProcessHandleCheckBox.UseVisualStyleBackColor = true; @@ -536,9 +536,9 @@ namespace Netch.Forms this.TUNTAPDNSLabel.AutoSize = true; this.TUNTAPDNSLabel.Location = new System.Drawing.Point(9, 112); this.TUNTAPDNSLabel.Name = "TUNTAPDNSLabel"; - this.TUNTAPDNSLabel.Size = new System.Drawing.Size(73, 17); + this.TUNTAPDNSLabel.Size = new System.Drawing.Size(34, 17); this.TUNTAPDNSLabel.TabIndex = 6; - this.TUNTAPDNSLabel.Text = "DNS Hijack"; + this.TUNTAPDNSLabel.Text = "DNS"; // // TUNTAPDNSTextBox // @@ -554,7 +554,7 @@ namespace Netch.Forms this.UseCustomDNSCheckBox.AutoSize = true; this.UseCustomDNSCheckBox.Location = new System.Drawing.Point(10, 139); this.UseCustomDNSCheckBox.Name = "UseCustomDNSCheckBox"; - this.UseCustomDNSCheckBox.Size = new System.Drawing.Size(127, 21); + this.UseCustomDNSCheckBox.Size = new System.Drawing.Size(125, 21); this.UseCustomDNSCheckBox.TabIndex = 8; this.UseCustomDNSCheckBox.Text = "Use custom DNS"; this.UseCustomDNSCheckBox.UseVisualStyleBackColor = true; @@ -562,12 +562,13 @@ namespace Netch.Forms // // ProxyDNSCheckBox // + this.ProxyDNSCheckBox.DataBindings.Add((new System.Windows.Forms.Binding("Visible", UseCustomDNSCheckBox, "Checked", true))); this.ProxyDNSCheckBox.AutoSize = true; this.ProxyDNSCheckBox.Location = new System.Drawing.Point(175, 139); this.ProxyDNSCheckBox.Name = "ProxyDNSCheckBox"; - this.ProxyDNSCheckBox.Size = new System.Drawing.Size(228, 21); + this.ProxyDNSCheckBox.Size = new System.Drawing.Size(89, 21); this.ProxyDNSCheckBox.TabIndex = 9; - this.ProxyDNSCheckBox.Text = "Proxy DNS in Proxy Rule IPs Mode"; + this.ProxyDNSCheckBox.Text = "Proxy DNS"; this.ProxyDNSCheckBox.UseVisualStyleBackColor = true; // // GlobalBypassIPsButton @@ -931,6 +932,7 @@ namespace Netch.Forms this.AioDNSListenPortLabel.Size = new System.Drawing.Size(69, 17); this.AioDNSListenPortLabel.TabIndex = 4; this.AioDNSListenPortLabel.Text = "Listen Port"; + this.AioDNSListenPortLabel.Visible = false; // // AioDNSListenPortTextBox // @@ -939,6 +941,7 @@ namespace Netch.Forms this.AioDNSListenPortTextBox.Size = new System.Drawing.Size(80, 23); this.AioDNSListenPortTextBox.TabIndex = 5; this.AioDNSListenPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.AioDNSListenPortTextBox.Visible = false; // // ControlButton // diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs index 6fdb3150..1c05eefb 100644 --- a/Netch/Forms/SettingForm.cs +++ b/Netch/Forms/SettingForm.cs @@ -129,9 +129,9 @@ namespace Netch.Forms s => { if (UseCustomDNSCheckBox.Checked) - Global.Settings.TUNTAP.HijackDNS = s; + Global.Settings.TUNTAP.DNS = s; }, - Global.Settings.TUNTAP.HijackDNS); + Global.Settings.TUNTAP.DNS); BindCheckBox(ProxyDNSCheckBox, b => Global.Settings.TUNTAP.ProxyDNS = b, Global.Settings.TUNTAP.ProxyDNS); @@ -216,7 +216,7 @@ namespace Netch.Forms private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object? sender, EventArgs? e) { if (UseCustomDNSCheckBox.Checked) - TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.HijackDNS; + TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS; else TUNTAPDNSTextBox.Text = "AioDNS"; } diff --git a/Netch/Interops/AioDNS.cs b/Netch/Interops/AioDNS.cs index 5ea87d09..e476ccf3 100644 --- a/Netch/Interops/AioDNS.cs +++ b/Netch/Interops/AioDNS.cs @@ -19,7 +19,7 @@ namespace Netch.Interops { return await Task.Run(Init).ConfigureAwait(false); } - + public static async Task FreeAsync() { await Task.Run(Free).ConfigureAwait(false); diff --git a/Netch/Interops/RouteHelper.cs b/Netch/Interops/RouteHelper.cs index 3c3c6f6c..059a0a36 100644 --- a/Netch/Interops/RouteHelper.cs +++ b/Netch/Interops/RouteHelper.cs @@ -1,9 +1,13 @@ using System.Net.Sockets; using System.Runtime.InteropServices; +using System.Threading; +using Windows.Win32.Foundation; +using Windows.Win32.NetworkManagement.IpHelper; +using static Windows.Win32.PInvoke; namespace Netch.Interops { - public static class RouteHelper + public static unsafe class RouteHelper { [DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)] public static extern ulong ConvertLuidToIndex(ulong id); @@ -11,8 +15,51 @@ namespace Netch.Interops [DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)] public static extern bool CreateIPv4(string address, string netmask, ulong index); - [DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)] - public static extern bool CreateUnicastIP(AddressFamily inet, string address, byte cidr, ulong index); + public static bool CreateUnicastIP(AddressFamily inet, string address, byte cidr, ulong index) + { + MIB_UNICASTIPADDRESS_ROW addr; + InitializeUnicastIpAddressEntry(&addr); + + addr.InterfaceIndex = (uint)index; + addr.OnLinkPrefixLength = cidr; + + if (inet == AddressFamily.InterNetwork) + { + addr.Address.Ipv4.sin_family = (ushort)ADDRESS_FAMILY.AF_INET; + if (inet_pton((int)inet, address, &addr.Address.Ipv4.sin_addr) == 0) + return false; + } + else if (inet == AddressFamily.InterNetworkV6) + { + addr.Address.Ipv6.sin6_family = (ushort)ADDRESS_FAMILY.AF_INET6; + if (inet_pton((int)inet, address, &addr.Address.Ipv6.sin6_addr) == 0) + return false; + } + else + { + return false; + } + + // Create a Handle to be notified of IP address changes + HANDLE handle = default; + using var obj = new Semaphore(1, 1); + void Callback(void* context, MIB_UNICASTIPADDRESS_ROW* row, MIB_NOTIFICATION_TYPE type) => obj.Release(1); + + // Use NotifyUnicastIpAddressChange to determine when the address is ready + obj.WaitOne(); + NotifyUnicastIpAddressChange((ushort)ADDRESS_FAMILY.AF_INET, Callback, null, new BOOLEAN(), ref handle); + + if (CreateUnicastIpAddressEntry(&addr) != 0) + { + // ignored return state because i feel great + CancelMibChangeNotify2(handle); + return false; + } + + obj.WaitOne(); + + return true; + } [DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)] public static extern bool RefreshIPTable(AddressFamily inet, ulong index); diff --git a/Netch/Interops/tun2socks.cs b/Netch/Interops/tun2socks.cs deleted file mode 100644 index 4d15a52e..00000000 --- a/Netch/Interops/tun2socks.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Serilog; - -namespace Netch.Interops -{ - public static class tun2socks - { - public enum NameList - { - TYPE_BYPBIND, - TYPE_BYPLIST, - TYPE_DNSADDR, - TYPE_ADAPMTU, - TYPE_TCPREST, - TYPE_TCPTYPE, - TYPE_TCPHOST, - TYPE_TCPUSER, - TYPE_TCPPASS, - TYPE_TCPMETH, - TYPE_TCPPROT, - TYPE_TCPPRPA, - TYPE_TCPOBFS, - TYPE_TCPOBPA, - TYPE_UDPREST, - TYPE_UDPTYPE, - TYPE_UDPHOST, - TYPE_UDPUSER, - TYPE_UDPPASS, - TYPE_UDPMETH, - TYPE_UDPPROT, - TYPE_UDPPRPA, - TYPE_UDPOBFS, - TYPE_UDPOBPA - } - - public static bool Dial(NameList name, string value) - { - Log.Verbose( $"[tun2socks] Dial {name}: {value}"); - return tun_dial(name, Encoding.UTF8.GetBytes(value)); - } - - public static bool Init() - { - Log.Verbose("[tun2socks] init"); - return tun_init(); - } - - public static async Task FreeAsync() - { - return await Task.Run(tun_free).ConfigureAwait(false); - } - - private const string tun2socks_bin = "tun2socks.bin"; - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - private static extern bool tun_dial(NameList name, byte[] value); - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - private static extern bool tun_init(); - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - private static extern bool tun_free(); - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - public static extern ulong tun_luid(); - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - private static extern ulong tun_getUP(); - - [DllImport(tun2socks_bin, CallingConvention = CallingConvention.Cdecl)] - private static extern ulong tun_getDL(); - } -} \ No newline at end of file diff --git a/Netch/Models/Settings/AioDNSConfig.cs b/Netch/Models/Settings/AioDNSConfig.cs index ff541c4c..50fd1af2 100644 --- a/Netch/Models/Settings/AioDNSConfig.cs +++ b/Netch/Models/Settings/AioDNSConfig.cs @@ -1,11 +1,14 @@ -namespace Netch.Models +using System.Text.Json.Serialization; + +namespace Netch.Models { public class AioDNSConfig { - public string ChinaDNS { get; set; } = $"tcp://{Constants.DefaultCNPrimaryDNS}"; + public string ChinaDNS { get; set; } = $"tcp://{Constants.DefaultCNPrimaryDNS}:53"; - public string OtherDNS { get; set; } = $"tcp://{Constants.DefaultPrimaryDNS}"; + public string OtherDNS { get; set; } = $"tcp://{Constants.DefaultPrimaryDNS}:53"; - public ushort ListenPort { get; set; } = 253; + [JsonIgnore] + public ushort ListenPort { get; set; } = 53; } } \ No newline at end of file diff --git a/Netch/Models/Settings/RedirectorConfig.cs b/Netch/Models/Settings/RedirectorConfig.cs index 89ab063b..36afd91a 100644 --- a/Netch/Models/Settings/RedirectorConfig.cs +++ b/Netch/Models/Settings/RedirectorConfig.cs @@ -14,7 +14,7 @@ public bool DNSProxy { get; set; } = true; - public string DNSHost { get; set; } = Constants.DefaultPrimaryDNS; + public string DNSHost { get; set; } = $"{Constants.DefaultPrimaryDNS}:53"; public int ICMPDelay { get; set; } = 10; diff --git a/Netch/Models/Settings/TUNConfig.cs b/Netch/Models/Settings/TUNConfig.cs index 5ae398b8..0c3b1926 100644 --- a/Netch/Models/Settings/TUNConfig.cs +++ b/Netch/Models/Settings/TUNConfig.cs @@ -15,7 +15,7 @@ namespace Netch.Models /// /// DNS /// - public string HijackDNS { get; set; } = $"tcp://{Constants.DefaultPrimaryDNS}"; + public string DNS { get; set; } = Constants.DefaultPrimaryDNS; /// /// 网关 diff --git a/Netch/NativeMethods.txt b/Netch/NativeMethods.txt index 1b515f2a..b55523f7 100644 --- a/Netch/NativeMethods.txt +++ b/Netch/NativeMethods.txt @@ -3,6 +3,11 @@ GetBestRoute GetExtendedTcpTable MIB_TCPTABLE_OWNER_PID ADDRESS_FAMILY +MIB_UNICASTIPADDRESS_ROW +InitializeUnicastIpAddressEntry +CreateUnicastIpAddressEntry +NotifyUnicastIpAddressChange +CancelMibChangeNotify2 // User32.dll SetWindowPos @@ -16,6 +21,7 @@ GetConsoleWindow // Ws2_32.dll ntohs +inet_pton // Windows.h // WIN32_ERROR \ No newline at end of file diff --git a/Netch/Resources/zh-CN b/Netch/Resources/zh-CN index 7bcd094a..4fe6ecda 100644 --- a/Netch/Resources/zh-CN +++ b/Netch/Resources/zh-CN @@ -152,7 +152,7 @@ "Netmask": "子网掩码", "Gateway": "网关", "Use custom DNS": "使用自定义 DNS", - "Proxy DNS in Proxy Rule IPs Mode": "在 代理规则IP 模式下代理 DNS", + "Proxy DNS": "代理 DNS", "Exit when closed": "关闭时退出", "Stop when exited": "退出时停止", "Global Bypass IPs": "全局直连 IP", diff --git a/Netch/Services/ModeService.cs b/Netch/Services/ModeService.cs index 9d9ee212..5a0b520e 100644 --- a/Netch/Services/ModeService.cs +++ b/Netch/Services/ModeService.cs @@ -63,7 +63,7 @@ namespace Netch.Services } } - private static void Sort() + private static void SortCollection() { // TODO better sort need to discuss // TODO replace Mode Collection type @@ -82,6 +82,12 @@ namespace Netch.Services mode.WriteFile(); } + public void Sort() + { + SortCollection(); + Global.MainForm.LoadModes(); + } + public static void Delete(Mode mode) { if (mode.FullName == null) diff --git a/Netch/Utils/NetworkInterfaceUtils.cs b/Netch/Utils/NetworkInterfaceUtils.cs index 663b0b16..2e79e8d7 100644 --- a/Netch/Utils/NetworkInterfaceUtils.cs +++ b/Netch/Utils/NetworkInterfaceUtils.cs @@ -32,19 +32,14 @@ namespace Netch.Utils return Get((int)route.dwForwardIfIndex); } - /// - /// - /// - /// - /// public static NetworkInterface Get(int interfaceIndex) { return NetworkInterface.GetAllNetworkInterfaces().First(n => n.GetIndex() == interfaceIndex); } - public static NetworkInterface Get(string description) + public static NetworkInterface Get(Func expression) { - return NetworkInterface.GetAllNetworkInterfaces().First(n => n.Description == description); + return NetworkInterface.GetAllNetworkInterfaces().First(expression); } public static void SetInterfaceMetric(int interfaceIndex, int? metric = null)