From 5730a67fb32b399f8e2be79e05d31aa28844faa9 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 May 2013 19:35:48 -0700 Subject: [PATCH] fixed bug where urlacl wouldn't register if firewall port was already open. --- NzbDrone.Common/FirewallAdapter.cs | 145 ++++++++++ NzbDrone.Common/IEnvironmentProvider.cs | 27 ++ NzbDrone.Common/NzbDrone.Common.csproj | 3 +- NzbDrone.Common/SecurityProvider.cs | 253 ------------------ NzbDrone.Common/UrlAclAdapter.cs | 66 +++++ NzbDrone.Update/NzbDrone.Update.csproj | 1 + ...ApplicationMode.cs => ApplicationModes.cs} | 0 NzbDrone/ApplicationServer.cs | 14 +- NzbDrone/NzbDrone.csproj | 2 +- 9 files changed, 252 insertions(+), 259 deletions(-) create mode 100644 NzbDrone.Common/FirewallAdapter.cs delete mode 100644 NzbDrone.Common/SecurityProvider.cs create mode 100644 NzbDrone.Common/UrlAclAdapter.cs rename NzbDrone/{ApplicationMode.cs => ApplicationModes.cs} (100%) diff --git a/NzbDrone.Common/FirewallAdapter.cs b/NzbDrone.Common/FirewallAdapter.cs new file mode 100644 index 000000000..26627d3f5 --- /dev/null +++ b/NzbDrone.Common/FirewallAdapter.cs @@ -0,0 +1,145 @@ +using System; +using NLog; +using NetFwTypeLib; + +namespace NzbDrone.Common +{ + public interface IFirewallAdapter + { + void MakeAccessible(); + bool IsNzbDronePortOpen(); + } + + public class FirewallAdapter : IFirewallAdapter + { + private readonly IConfigFileProvider _configFileProvider; + private readonly Logger _logger; + + public FirewallAdapter(IConfigFileProvider configFileProvider, Logger logger) + { + _configFileProvider = configFileProvider; + _logger = logger; + } + + public void MakeAccessible() + { + int port = 0; + + if (IsFirewallEnabled()) + { + if (IsNzbDronePortOpen()) + { + _logger.Trace("NzbDrone port is already open, skipping."); + return; + } + + CloseFirewallPort(); + + //Open the new port + OpenFirewallPort(_configFileProvider.Port); + } + } + + + public bool IsNzbDronePortOpen() + { + try + { + var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); + + var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); + + if (!mgr.LocalPolicy.CurrentProfile.FirewallEnabled) + return false; + + var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; + + foreach (INetFwOpenPort p in ports) + { + if (p.Port == _configFileProvider.Port) + return true; + } + } + catch (Exception ex) + { + _logger.WarnException("Failed to check for open port in firewall", ex); + } + return false; + } + + + + private void OpenFirewallPort(int portNumber) + { + try + { + var type = Type.GetTypeFromProgID("HNetCfg.FWOpenPort", false); + var port = Activator.CreateInstance(type) as INetFwOpenPort; + + port.Port = portNumber; + port.Name = "NzbDrone"; + port.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; + port.Enabled = true; + + var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); + var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); + var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; + + ports.Add(port); + } + catch (Exception ex) + { + _logger.WarnException("Failed to open port in firewall for NzbDrone " + portNumber, ex); + } + } + + private int CloseFirewallPort() + { + try + { + var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); + var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); + var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; + + var portNumber = 8989; + + foreach (INetFwOpenPort p in ports) + { + if (p.Name == "NzbDrone") + { + portNumber = p.Port; + break; + } + } + + if (portNumber != _configFileProvider.Port) + { + ports.Remove(portNumber, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP); + return portNumber; + } + } + catch (Exception ex) + { + _logger.WarnException("Failed to close port in firewall for NzbDrone", ex); + } + return 0; + } + + private bool IsFirewallEnabled() + { + try + { + var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); + var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); + return mgr.LocalPolicy.CurrentProfile.FirewallEnabled; + } + catch (Exception ex) + { + _logger.WarnException("Failed to check if the firewall is enabled", ex); + return false; + } + } + + + } +} diff --git a/NzbDrone.Common/IEnvironmentProvider.cs b/NzbDrone.Common/IEnvironmentProvider.cs index 308f44ca8..e3fec5dca 100644 --- a/NzbDrone.Common/IEnvironmentProvider.cs +++ b/NzbDrone.Common/IEnvironmentProvider.cs @@ -2,12 +2,15 @@ using System.Diagnostics; using System.IO; using System.Reflection; +using System.Security.Principal; +using NLog; namespace NzbDrone.Common { public interface IEnvironmentProvider { bool IsUserInteractive { get; } + bool IsAdmin { get; } string WorkingDirectory { get; } string SystemTemp { get; } Version Version { get; } @@ -18,10 +21,17 @@ namespace NzbDrone.Common public class EnvironmentProvider : IEnvironmentProvider { + private readonly Logger _logger; private static readonly string ProcessName = Process.GetCurrentProcess().ProcessName.ToLower(); private static readonly IEnvironmentProvider Instance = new EnvironmentProvider(); + + public EnvironmentProvider() + { + _logger = LogManager.GetCurrentClassLogger(); + } + public static bool IsProduction { get @@ -75,6 +85,23 @@ namespace NzbDrone.Common get { return Environment.UserInteractive; } } + public bool IsAdmin + { + get + { + try + { + var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + catch (Exception ex) + { + _logger.WarnException("Error checking if the current user is an administrator.", ex); + return false; + } + } + } + public virtual string WorkingDirectory { get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NzbDrone"); } diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index fcad0d4a1..942cfac53 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -149,11 +149,12 @@ - + + diff --git a/NzbDrone.Common/SecurityProvider.cs b/NzbDrone.Common/SecurityProvider.cs deleted file mode 100644 index 87b726b2d..000000000 --- a/NzbDrone.Common/SecurityProvider.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Security.Principal; -using NLog; -#if __MonoCS__ -#else -using NetFwTypeLib; -#endif - -namespace NzbDrone.Common -{ - public interface ISecurityProvider - { - void MakeAccessible(); - bool IsCurrentUserAdmin(); - bool IsNzbDronePortOpen(); - bool IsNzbDroneUrlRegistered(); - } - - public class SecurityProvider : ISecurityProvider - { - private readonly IConfigFileProvider _configFileProvider; - private readonly IEnvironmentProvider _environmentProvider; - private readonly IProcessProvider _processProvider; - private readonly Logger _logger; - - public SecurityProvider(IConfigFileProvider configFileProvider, IEnvironmentProvider environmentProvider, - IProcessProvider processProvider, Logger logger) - { - _configFileProvider = configFileProvider; - _environmentProvider = environmentProvider; - _processProvider = processProvider; - _logger = logger; - } - - public void MakeAccessible() - { - if (!IsCurrentUserAdmin ()) - { - _logger.Trace ("User is not an admin, skipping."); - return; - } - - int port = 0; - - if (IsFirewallEnabled ()) - { - if (IsNzbDronePortOpen ()) - { - _logger.Trace ("NzbDrone port is already open, skipping."); - return; - } - - //Close any old ports - port = CloseFirewallPort (); - - //Open the new port - OpenFirewallPort (_configFileProvider.Port); - } - - //Skip Url Register if not Vista or 7 - if (_environmentProvider.GetOsVersion ().Major < 6) - return; - - //Unregister Url (if port != 0) - if (port != 0) - UnregisterUrl(port); - - //Register Url - RegisterUrl(_configFileProvider.Port); - } - - public bool IsCurrentUserAdmin() - { - try { - var principal = new WindowsPrincipal (WindowsIdentity.GetCurrent ()); - return principal.IsInRole (WindowsBuiltInRole.Administrator); - } catch (Exception ex) { - _logger.WarnException ("Error checking if the current user is an administrator.", ex); - return false; - } - } - - public bool IsNzbDronePortOpen() - { -#if __MonoCS__ -#else - - try { - var netFwMgrType = Type.GetTypeFromProgID ("HNetCfg.FwMgr", false); - - var mgr = (INetFwMgr)Activator.CreateInstance (netFwMgrType); - - if (!mgr.LocalPolicy.CurrentProfile.FirewallEnabled) - return false; - - var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; - - foreach (INetFwOpenPort p in ports) { - if (p.Port == _configFileProvider.Port) - return true; - } - } - catch (Exception ex) { - _logger.WarnException ("Failed to check for open port in firewall", ex); - } -#endif - return false; - } - - public bool IsNzbDroneUrlRegistered() - { - return CheckIfUrlIsRegisteredUrl(_configFileProvider.Port); - } - - private void OpenFirewallPort(int portNumber) - { -#if __MonoCS__ - return true; -#else - try { - var type = Type.GetTypeFromProgID ("HNetCfg.FWOpenPort", false); - var port = Activator.CreateInstance (type) as INetFwOpenPort; - - port.Port = portNumber; - port.Name = "NzbDrone"; - port.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; - port.Enabled = true; - - var netFwMgrType = Type.GetTypeFromProgID ("HNetCfg.FwMgr", false); - var mgr = (INetFwMgr)Activator.CreateInstance (netFwMgrType); - var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; - - ports.Add (port); - } - catch (Exception ex) { - _logger.WarnException ("Failed to open port in firewall for NzbDrone " + portNumber, ex); - } -#endif - } - - private int CloseFirewallPort() - { - -#if __MonoCS__ -#else - - try { - var netFwMgrType = Type.GetTypeFromProgID ("HNetCfg.FwMgr", false); - var mgr = (INetFwMgr)Activator.CreateInstance (netFwMgrType); - var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; - - var portNumber = 8989; - - foreach (INetFwOpenPort p in ports) { - if (p.Name == "NzbDrone") { - portNumber = p.Port; - break; - } - } - - if (portNumber != _configFileProvider.Port) - { - ports.Remove (portNumber, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP); - return portNumber; - } - } - catch (Exception ex) - { - _logger.WarnException ("Failed to close port in firewall for NzbDrone", ex); - } -#endif - return 0; - } - - private bool IsFirewallEnabled() - { -#if __MonoCS__ - return true; -#else - - try { - var netFwMgrType = Type.GetTypeFromProgID ("HNetCfg.FwMgr", false); - var mgr = (INetFwMgr)Activator.CreateInstance (netFwMgrType); - return mgr.LocalPolicy.CurrentProfile.FirewallEnabled; - } - catch (Exception ex) - { - _logger.WarnException ("Failed to check if the firewall is enabled", ex); - return false; - } -#endif - } - - private void RegisterUrl(int portNumber) - { - var arguments = String.Format("http add urlacl http://*:{0}/ user=EVERYONE", portNumber); - RunNetsh(arguments); - } - - private void UnregisterUrl(int portNumber) - { - var arguments = String.Format("http delete urlacl http://*:{0}/", portNumber); - RunNetsh(arguments); - } - - private bool CheckIfUrlIsRegisteredUrl(int portNumber) - { - var url = String.Format("http://*:{0}/", portNumber); - var arguments = String.Format("http show urlacl url=\"{0}\"", url); - var output = RunNetsh(arguments); - - if(String.IsNullOrWhiteSpace(output)) - { - _logger.Error("netsh output is invalid for arguments: {0}", arguments); - } - - if(!output.Contains(url)) - { - _logger.Trace("Url has not already been registered"); - return false; - } - - _logger.Trace("Url has already been registered!"); - return true; - } - - private string RunNetsh(string arguments) - { - try - { - var startInfo = new ProcessStartInfo() - { - RedirectStandardOutput = true, - UseShellExecute = false, - FileName = "netsh.exe", - Arguments = arguments - }; - - var process = _processProvider.Start(startInfo); - process.WaitForExit(5000); - return process.StandardOutput.ReadToEnd(); - } - catch (Exception ex) - { - _logger.WarnException("Error executing netsh with arguments: " + arguments, ex); - } - - return null; - } - } -} diff --git a/NzbDrone.Common/UrlAclAdapter.cs b/NzbDrone.Common/UrlAclAdapter.cs new file mode 100644 index 000000000..63be66c09 --- /dev/null +++ b/NzbDrone.Common/UrlAclAdapter.cs @@ -0,0 +1,66 @@ +using System; +using System.Diagnostics; +using NLog; + +namespace NzbDrone.Common +{ + public interface IUrlAclAdapter + { + void RefreshRegistration(); + } + + public class UrlAclAdapter : IUrlAclAdapter + { + private readonly IProcessProvider _processProvider; + private readonly IConfigFileProvider _configFileProvider; + private readonly IEnvironmentProvider _environmentProvider; + private readonly Logger _logger; + + public UrlAclAdapter(IProcessProvider processProvider, IConfigFileProvider configFileProvider, IEnvironmentProvider environmentProvider, Logger logger) + { + _processProvider = processProvider; + _configFileProvider = configFileProvider; + _environmentProvider = environmentProvider; + _logger = logger; + } + + public void RefreshRegistration() + { + if (_environmentProvider.GetOsVersion().Major < 6) + return; + + RegisterUrl(_configFileProvider.Port); + } + + + private void RegisterUrl(int portNumber) + { + var arguments = String.Format("http add urlacl http://*:{0}/ user=EVERYONE", portNumber); + RunNetsh(arguments); + } + + private string RunNetsh(string arguments) + { + try + { + var startInfo = new ProcessStartInfo() + { + RedirectStandardOutput = true, + UseShellExecute = false, + FileName = "netsh.exe", + Arguments = arguments + }; + + var process = _processProvider.Start(startInfo); + process.WaitForExit(5000); + return process.StandardOutput.ReadToEnd(); + } + catch (Exception ex) + { + _logger.WarnException("Error executing netsh with arguments: " + arguments, ex); + } + + return null; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Update/NzbDrone.Update.csproj b/NzbDrone.Update/NzbDrone.Update.csproj index d58956c78..97e719d0a 100644 --- a/NzbDrone.Update/NzbDrone.Update.csproj +++ b/NzbDrone.Update/NzbDrone.Update.csproj @@ -76,6 +76,7 @@ Always + Designer Designer diff --git a/NzbDrone/ApplicationMode.cs b/NzbDrone/ApplicationModes.cs similarity index 100% rename from NzbDrone/ApplicationMode.cs rename to NzbDrone/ApplicationModes.cs diff --git a/NzbDrone/ApplicationServer.cs b/NzbDrone/ApplicationServer.cs index b741c9e30..63b67109a 100644 --- a/NzbDrone/ApplicationServer.cs +++ b/NzbDrone/ApplicationServer.cs @@ -19,20 +19,22 @@ namespace NzbDrone private readonly IHostController _hostController; private readonly IProcessProvider _processProvider; private readonly PriorityMonitor _priorityMonitor; - private readonly SecurityProvider _securityProvider; + private readonly IFirewallAdapter _firewallAdapter; + private readonly IUrlAclAdapter _urlAclAdapter; private readonly Logger _logger; public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, IEnvironmentProvider environmentProvider, IProcessProvider processProvider, PriorityMonitor priorityMonitor, - SecurityProvider securityProvider, Logger logger) + IFirewallAdapter firewallAdapter, IUrlAclAdapter urlAclAdapter, Logger logger) { _configFileProvider = configFileProvider; _hostController = hostController; _environmentProvider = environmentProvider; _processProvider = processProvider; _priorityMonitor = priorityMonitor; - _securityProvider = securityProvider; + _firewallAdapter = firewallAdapter; + _urlAclAdapter = urlAclAdapter; _logger = logger; } @@ -43,8 +45,12 @@ namespace NzbDrone public void Start() { - _securityProvider.MakeAccessible(); + if (_environmentProvider.IsAdmin) + { + _urlAclAdapter.RefreshRegistration(); + _firewallAdapter.MakeAccessible(); + } _hostController.StartServer(); if (_environmentProvider.IsUserInteractive && _configFileProvider.LaunchBrowser) diff --git a/NzbDrone/NzbDrone.csproj b/NzbDrone/NzbDrone.csproj index df782eb25..22ca1b196 100644 --- a/NzbDrone/NzbDrone.csproj +++ b/NzbDrone/NzbDrone.csproj @@ -138,7 +138,7 @@ Component - +