From 8b7ce23a829bddbe9622930995852dc486c30250 Mon Sep 17 00:00:00 2001 From: ChsBuffer <33744752+chsbuffer@users.noreply.github.com> Date: Tue, 22 Sep 2020 17:57:26 +0800 Subject: [PATCH] Add TAP Adapter Internet Network Sharing Setting(ICS) --- Netch/Controllers/ICSController.cs | 174 +++++++++++++++ Netch/Controllers/Mode/TUNTAPController.cs | 2 +- Netch/Forms/SettingForm.Designer.cs | 16 ++ Netch/Forms/SettingForm.cs | 27 ++- Netch/Models/WinFW/NetworkConnection.cs | 114 ++++++++++ .../WinFW/NetworkConnectionCollection.cs | 201 ++++++++++++++++++ Netch/Netch.csproj | 9 + 7 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 Netch/Controllers/ICSController.cs create mode 100644 Netch/Models/WinFW/NetworkConnection.cs create mode 100644 Netch/Models/WinFW/NetworkConnectionCollection.cs diff --git a/Netch/Controllers/ICSController.cs b/Netch/Controllers/ICSController.cs new file mode 100644 index 00000000..16d3aa4a --- /dev/null +++ b/Netch/Controllers/ICSController.cs @@ -0,0 +1,174 @@ +using System; +using System.Management; +using Netch.Utils; +using NETCONLib; +using WinFW; + +namespace Netch.Controllers +{ + public class ICSController + { + public static bool Enabled + { + get + { + TUNTAPController.SearchTapAdapter(); + foreach (NetworkConnection connection in new NetworkConnectionCollection()) + { + if (connection.DeviceName == Global.TUNTAP.Adapter.Description) + { + return connection.SharingEnabled; + } + } + + return false; + } + } + + public static bool Enable() + { + Utils.Utils.SearchOutboundAdapter(); + TUNTAPController.SearchTapAdapter(); + + if (Global.TUNTAP.Adapter == null || Global.Outbound.Adapter == null) + { + return false; + } + + try + { + CleanupWMISharingEntries(); + + #region Save Outbound IP Config + + var wmi = new ManagementClass("Win32_NetworkAdapterConfiguration"); + var moc = wmi.GetInstances(); + + var dhcpEnabled = true; + string[] ipAddress = null; + string[] subnetMask = null; + string[] gateway = null; + ushort[] gatewayMetric = null; + string[] dns = null; + + ManagementObject outboundWmi = null; + foreach (ManagementObject mo in moc) + { + if (((string) mo["Caption"]).EndsWith(Global.Outbound.Adapter.Description)) + { + outboundWmi = mo; + if (!(dhcpEnabled = (bool) mo["DHCPEnabled"])) + { + ipAddress = (string[]) mo["IPAddress"]; + subnetMask = (string[]) mo["IPSubnet"]; + gateway = (string[]) mo["DefaultIPGateway"]; + gatewayMetric = (ushort[]) mo["GatewayCostMetric"]; + dns = (string[]) mo["DNSServerSearchOrder"]; + + ipAddress = new[] {ipAddress[0]}; + subnetMask = new[] {subnetMask[0]}; + } + + break; + } + } + + #endregion + + #region Setting ICS + + foreach (NetworkConnection connection in new NetworkConnectionCollection()) + { + if (connection.DeviceName == Global.TUNTAP.Adapter.Description) + { + if (connection.SharingEnabled) + connection.DisableSharing(); + connection.EnableSharing(tagSHARINGCONNECTIONTYPE.ICSSHARINGTYPE_PUBLIC); + } + else if (connection.DeviceName == Global.Outbound.Adapter.Description) + { + if (connection.SharingEnabled) + connection.DisableSharing(); + connection.EnableSharing(tagSHARINGCONNECTIONTYPE.ICSSHARINGTYPE_PRIVATE); + } + } + + #endregion + + #region Reset Outbound IP Config + + if (dhcpEnabled) + { + outboundWmi.InvokeMethod("EnableDHCP", null, null); + } + else + { + //Set static IP and subnet mask + var newIP = outboundWmi.GetMethodParameters("EnableStatic"); + newIP["IPAddress"] = ipAddress; + newIP["SubnetMask"] = subnetMask; + outboundWmi.InvokeMethod("EnableStatic", newIP, null); + //Set default gateway + var newGateway = outboundWmi.GetMethodParameters("SetGateways"); + newGateway["DefaultIPGateway"] = gateway; + newGateway["GatewayCostMetric"] = gatewayMetric; + outboundWmi.InvokeMethod("SetGateways", newGateway, null); + //Set dns servers + var newDNS = outboundWmi.GetMethodParameters("SetDNSServerSearchOrder"); + newDNS["DNSServerSearchOrder"] = dns; + outboundWmi.InvokeMethod("SetDNSServerSearchOrder", newDNS, null); + } + + #endregion + + return true; + } + catch (Exception e) + { + try + { + Disable(); + } + catch + { + // ignored + } + + Logging.Error($"网络连接共享设置失败: {e}"); + + return false; + } + } + + public static void Disable() + { + foreach (NetworkConnection connection in new NetworkConnectionCollection()) + { + if (connection.SharingEnabled) + connection.DisableSharing(); + } + + CleanupWMISharingEntries(); + } + + private static void CleanupWMISharingEntries() + { + var scope = new ManagementScope("root\\Microsoft\\HomeNet"); + scope.Connect(); + + var options = new PutOptions(); + options.Type = PutType.UpdateOnly; + + var query = new ObjectQuery("SELECT * FROM HNet_ConnectionProperties"); + var srchr = new ManagementObjectSearcher(scope, query); + foreach (ManagementObject entry in srchr.Get()) + { + if ((bool) entry["IsIcsPrivate"]) + entry["IsIcsPrivate"] = false; + if ((bool) entry["IsIcsPublic"]) + entry["IsIcsPublic"] = false; + entry.Put(options); + } + } + } +} \ No newline at end of file diff --git a/Netch/Controllers/Mode/TUNTAPController.cs b/Netch/Controllers/Mode/TUNTAPController.cs index fb423dac..8eb97931 100644 --- a/Netch/Controllers/Mode/TUNTAPController.cs +++ b/Netch/Controllers/Mode/TUNTAPController.cs @@ -224,7 +224,7 @@ namespace Netch.Controllers } else { - pDNSController.Start(); + var _ = pDNSController.Start(); dns = "127.0.0.1"; } diff --git a/Netch/Forms/SettingForm.Designer.cs b/Netch/Forms/SettingForm.Designer.cs index 18bb10bf..bd9ac273 100644 --- a/Netch/Forms/SettingForm.Designer.cs +++ b/Netch/Forms/SettingForm.Designer.cs @@ -40,6 +40,7 @@ this.TUNTAPGroupBox = new System.Windows.Forms.GroupBox(); this.UseFakeDNSCheckBox = new System.Windows.Forms.CheckBox(); this.ProxyDNSCheckBox = new System.Windows.Forms.CheckBox(); + this.ICSCheckBox = new System.Windows.Forms.CheckBox(); this.UseCustomDNSCheckBox = new System.Windows.Forms.CheckBox(); this.TUNTAPDNSLabel = new System.Windows.Forms.Label(); this.TUNTAPDNSTextBox = new System.Windows.Forms.TextBox(); @@ -160,6 +161,7 @@ // this.TUNTAPGroupBox.Controls.Add(this.UseFakeDNSCheckBox); this.TUNTAPGroupBox.Controls.Add(this.ProxyDNSCheckBox); + this.TUNTAPGroupBox.Controls.Add(this.ICSCheckBox); this.TUNTAPGroupBox.Controls.Add(this.UseCustomDNSCheckBox); this.TUNTAPGroupBox.Controls.Add(this.TUNTAPDNSLabel); this.TUNTAPGroupBox.Controls.Add(this.TUNTAPDNSTextBox); @@ -197,6 +199,18 @@ this.ProxyDNSCheckBox.Text = "Proxy DNS in Mode 2"; this.ProxyDNSCheckBox.UseVisualStyleBackColor = true; // + // ICSCheckBox + // + this.ICSCheckBox.AutoSize = true; + this.ICSCheckBox.Location = new System.Drawing.Point(261, 160); + this.ICSCheckBox.Name = "ICSCheckBox"; + this.ICSCheckBox.Size = new System.Drawing.Size(46, 21); + this.ICSCheckBox.TabIndex = 5; + this.ICSCheckBox.Text = "ICS"; + this.ICSCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.ICSCheckBox.UseVisualStyleBackColor = true; + this.ICSCheckBox.CheckedChanged += new System.EventHandler(this.ICSCheckBox_CheckedChanged); + // // UseCustomDNSCheckBox // this.UseCustomDNSCheckBox.AutoSize = true; @@ -559,6 +573,8 @@ this.ResumeLayout(false); } + private System.Windows.Forms.CheckBox ICSCheckBox; + #endregion private System.Windows.Forms.GroupBox PortGroupBox; diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs index 417ff934..d92ce54c 100644 --- a/Netch/Forms/SettingForm.cs +++ b/Netch/Forms/SettingForm.cs @@ -2,7 +2,9 @@ using System.IO; using System.Linq; using System.Net; +using System.Threading.Tasks; using System.Windows.Forms; +using Netch.Controllers; using Netch.Utils; using TaskScheduler; @@ -36,6 +38,7 @@ namespace Netch.Forms TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null); ProxyDNSCheckBox.Checked = Global.Settings.TUNTAP.ProxyDNS; UseFakeDNSCheckBox.Checked = Global.Settings.TUNTAP.UseFakeDNS; + ICSCheckBox.Checked = ICSController.Enabled; // Behavior ExitWhenClosedCheckBox.Checked = Global.Settings.ExitWhenClosed; @@ -64,7 +67,9 @@ namespace Netch.Forms if (UseCustomDNSCheckBox.Checked) { - TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() ? Global.Settings.TUNTAP.DNS.Aggregate((current, ip) => $"{current},{ip}") : "1.1.1.1"; + TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() + ? Global.Settings.TUNTAP.DNS.Aggregate((current, ip) => $"{current},{ip}") + : "1.1.1.1"; } else { @@ -195,7 +200,8 @@ namespace Netch.Forms if (UseCustomDNSCheckBox.Checked) { - dns = TUNTAPDNSTextBox.Text.Split(',').Where(s => !string.IsNullOrEmpty(s)).Select(s => s.Trim()).ToArray(); + dns = TUNTAPDNSTextBox.Text.Split(',').Where(s => !string.IsNullOrEmpty(s)).Select(s => s.Trim()) + .ToArray(); if (dns.Any()) { foreach (var ip in dns) @@ -401,5 +407,22 @@ namespace Netch.Forms MessageBoxX.Show(i18N.Translate("Saved")); Close(); } + + private async void ICSCheckBox_CheckedChanged(object sender, EventArgs e) + { + ICSCheckBox.Enabled = false; + await Task.Run(() => + { + if (ICSCheckBox.Checked) + { + ICSCheckBox.Checked = ICSController.Enable(); + } + else + { + ICSController.Disable(); + } + }); + ICSCheckBox.Enabled = true; + } } } \ No newline at end of file diff --git a/Netch/Models/WinFW/NetworkConnection.cs b/Netch/Models/WinFW/NetworkConnection.cs new file mode 100644 index 00000000..394b16ab --- /dev/null +++ b/Netch/Models/WinFW/NetworkConnection.cs @@ -0,0 +1,114 @@ +using System; +using NETCONLib; + +namespace WinFW +{ + public class NetworkConnection : INetConnection, INetConnectionProps, INetSharingConfiguration + { + private readonly INetConnection _icsConn; + + private readonly NetSharingManager _icsMgr; + + public NetworkConnection(INetConnection icsConnection) + { + _icsMgr = new NetSharingManagerClass(); + _icsConn = icsConnection; + } + + public void Connect() + { + _icsConn.Connect(); + } + + public void Delete() + { + _icsConn.Delete(); + } + + public void Duplicate(string pszwDuplicateName, out INetConnection ppCon) + { + _icsConn.Duplicate(pszwDuplicateName, out ppCon); + } + + public void Disconnect() + { + _icsConn.Disconnect(); + } + + public void GetProperties(IntPtr ppProps) + { + _icsConn.GetProperties(ppProps); + } + + public void Rename(string pszwNewName) + { + _icsConn.Rename(pszwNewName); + } + + public void GetUiObjectClassId(out Guid pclsid) + { + _icsConn.GetUiObjectClassId(out pclsid); + } + + public uint Characteristics => _icsMgr.NetConnectionProps[_icsConn].Characteristics; + + public string DeviceName => _icsMgr.NetConnectionProps[_icsConn].DeviceName; + + public string Guid => _icsMgr.NetConnectionProps[_icsConn].Guid; + + public tagNETCON_MEDIATYPE MediaType => _icsMgr.NetConnectionProps[_icsConn].MediaType; + + public string Name => _icsMgr.NetConnectionProps[_icsConn].Name; + + public tagNETCON_STATUS Status => _icsMgr.NetConnectionProps[_icsConn].Status; + + public INetSharingPortMapping AddPortMapping(string bstrName, byte ucIPProtocol, ushort usExternalPort, + ushort usInternalPort, uint dwOptions, string bstrTargetNameOrIPAddress, tagICS_TARGETTYPE eTargetType) + { + return _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].AddPortMapping(bstrName, + ucIPProtocol, usExternalPort, usInternalPort, dwOptions, bstrTargetNameOrIPAddress, eTargetType); + } + + public void DisableInternetFirewall() => _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].DisableInternetFirewall(); + + public void DisableSharing() + { + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].DisableSharing(); + } + + public void EnableInternetFirewall() + { + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].EnableInternetFirewall(); + } + + public void EnableSharing(tagSHARINGCONNECTIONTYPE Type) + { + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].EnableSharing(Type); + } + + public void RemovePortMapping(INetSharingPortMapping pMapping) + { + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].RemovePortMapping(pMapping); + } + + public bool InternetFirewallEnabled => + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn] + .InternetFirewallEnabled; + + public INetSharingPortMappingCollection get_EnumPortMappings(tagSHARINGCONNECTION_ENUM_FLAGS Flags) + { + return _icsMgr.INetSharingConfigurationForINetConnection[_icsConn] + .EnumPortMappings[Flags]; + } + + public tagSHARINGCONNECTIONTYPE SharingConnectionType => + _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].SharingConnectionType; + + public bool SharingEnabled => _icsMgr.INetSharingConfigurationForINetConnection[_icsConn].SharingEnabled; + + public INetSharingConfiguration NetSharingConfigurationForINetConnection() + { + return _icsMgr.INetSharingConfigurationForINetConnection[_icsConn]; + } + } +} \ No newline at end of file diff --git a/Netch/Models/WinFW/NetworkConnectionCollection.cs b/Netch/Models/WinFW/NetworkConnectionCollection.cs new file mode 100644 index 00000000..89ad9b3b --- /dev/null +++ b/Netch/Models/WinFW/NetworkConnectionCollection.cs @@ -0,0 +1,201 @@ +using System.Collections; +using System.Linq; +using System.Management; +using NETCONLib; + +namespace WinFW +{ + /// + /// A collection that stores 'NetworkConnection' objects. + /// + public class NetworkConnectionCollection : CollectionBase + { + /// + /// Initializes a new instance of 'NetworkConnectionCollection'. + /// + public NetworkConnectionCollection() + { + NetSharingManager icsMgr = new NetSharingManagerClass(); + + foreach (var icsConn in icsMgr.EnumEveryConnection.Cast()) + { + Add(new NetworkConnection(icsConn)); + } + } + + /// + /// Represents the 'NetworkConnection' item at the specified index position. + /// + /// + /// The zero-based index of the entry to locate in the collection. + /// + /// + /// The entry at the specified index of the collection. + /// + public NetworkConnection this[int intIndex] + { + get => (NetworkConnection) List[intIndex]; + set => List[intIndex] = value; + } + + /// + /// Adds a 'NetworkConnection' item with the specified value to the 'NetworkConnectionCollection' + /// + /// + /// The 'NetworkConnection' to add. + /// + /// + /// The index at which the new element was inserted. + /// + public int Add(NetworkConnection conValue) + { + return List.Add(conValue); + } + + /// + /// Copies the elements of an array at the end of this instance of 'NetworkConnectionCollection'. + /// + /// + /// An array of 'NetworkConnection' objects to add to the collection. + /// + public void AddRange(NetworkConnection[] conValue) + { + checked + { + foreach (var t in conValue) + Add(t); + } + } + + /// + /// Adds the contents of another 'NetworkConnectionCollection' at the end of this instance. + /// + /// + /// A 'NetworkConnectionCollection' containing the objects to add to the collection. + /// + public void AddRange(NetworkConnectionCollection conValue) + { + checked + { + for (var intCounter = 0; intCounter < conValue.Count; intCounter++) Add(conValue[intCounter]); + } + } + + /// + /// Gets a value indicating whether the 'NetworkConnectionCollection' contains the specified value. + /// + /// + /// The item to locate. + /// + /// + /// True if the item exists in the collection; false otherwise. + /// + public bool Contains(NetworkConnection conValue) + { + return List.Contains(conValue); + } + + /// + /// Copies the 'NetworkConnectionCollection' values to a one-dimensional System.Array + /// instance starting at the specified array index. + /// + /// + /// The one-dimensional System.Array that represents the copy destination. + /// + /// + /// The index in the array where copying begins. + /// + public void CopyTo(NetworkConnection[] conArray, int intIndex) + { + List.CopyTo(conArray, intIndex); + } + + /// + /// Returns the index of a 'NetworkConnection' object in the collection. + /// + /// + /// The 'NetworkConnection' object whose index will be retrieved. + /// + /// + /// If found, the index of the value; otherwise, -1. + /// + public int IndexOf(NetworkConnection conValue) + { + return List.IndexOf(conValue); + } + + /// + /// Inserts an existing 'NetworkConnection' into the collection at the specified index. + /// + /// + /// The zero-based index where the new item should be inserted. + /// + /// + /// The item to insert. + /// + public void Insert(int intIndex, NetworkConnection conValue) + { + List.Insert(intIndex, conValue); + } + + /// + /// Returns an enumerator that can be used to iterate through + /// the 'NetworkConnectionCollection'. + /// + public new ConnectionEnumerator GetEnumerator() + { + return new ConnectionEnumerator(this); + } + + /// + /// Removes a specific item from the 'NetworkConnectionCollection'. + /// + /// + /// The item to remove from the 'NetworkConnectionCollection'. + /// + public void Remove(NetworkConnection conValue) + { + List.Remove(conValue); + } + + /// + /// A strongly typed enumerator for 'NetworkConnectionCollection' + /// + public class ConnectionEnumerator : IEnumerator + { + private readonly IEnumerator iEnBase; + + private readonly IEnumerable iEnLocal; + + /// + /// Enumerator constructor + /// + public ConnectionEnumerator(NetworkConnectionCollection conMappings) + { + iEnLocal = conMappings; + iEnBase = iEnLocal.GetEnumerator(); + } + + /// + /// Gets the current element from the collection + /// + public object Current => iEnBase.Current; + + /// + /// Advances the enumerator to the next element of the collection + /// + public bool MoveNext() + { + return iEnBase.MoveNext(); + } + + /// + /// Sets the enumerator to the first element in the collection + /// + public void Reset() + { + iEnBase.Reset(); + } + } + } +} \ No newline at end of file diff --git a/Netch/Netch.csproj b/Netch/Netch.csproj index 0e931ebe..161fc70e 100644 --- a/Netch/Netch.csproj +++ b/Netch/Netch.csproj @@ -54,6 +54,14 @@ tlbimp false + + {43E734CA-043D-4A70-9A2C-A8F254063D91} + 1 + 0 + 0 + tlbimp + False + @@ -73,6 +81,7 @@ +