/// <summary> /// Run the TcpForwarder as background application. This method will also be called when running as a service. /// In this mode, it will read "configuration.xml" to create TcpConnectionForwarder objects and run them. /// </summary> private static async Task RunBackgroundAsync(IBackgroundLogger logger) { IDictionary <long, TcpConnectionForwarder> forwarders = new SortedDictionary <long, TcpConnectionForwarder>(); List <ForwarderConfiguration> configs = GetConfiguration(logger); StringBuilder logSb = new StringBuilder(); // for logging all created forwarders Random seedGenerator = new Random(); // Create a forwarder for each config. long nextId = 0; foreach (ForwarderConfiguration config in configs) { long id = nextId++; TcpConnectionForwarder forwarder; try { forwarder = new TcpConnectionForwarder(seedGenerator.Next(), config.RemoteHost, config.RemotePort, config.LocalPort, config.RemoteSslHostname, TcpConnectionForwarderUtils.sslProtocols, config.LocalSslCert, TcpConnectionForwarderUtils.sslProtocols, config.MaxConcurrentConnections); } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } logger.LogError("Error when creating forwarder with ID " + id.ToString(CultureInfo.InvariantCulture) + ":\r\n" + FormatExceptionForLog(ex)); continue; } forwarders.Add(id, forwarder); // Log the creation of a forwarder if (logSb.Length > 0) { logSb.Append("\r\n"); } logSb.Append("[ID: ").Append(id.ToString(CultureInfo.InvariantCulture)) .Append("] maxConcurrentConnections = ").Append(config.MaxConcurrentConnections) .Append(", remoteHost = ").Append(config.RemoteHost) .Append(", remotePort = " + config.RemotePort.ToString(CultureInfo.InvariantCulture)) .Append(", localPort = " + config.LocalPort.ToString(CultureInfo.InvariantCulture)); if (config.LocalSslCert != null) { logSb.Append("; Server SSL certificate:\r\n").Append(config.LocalSslCert.ToString()); } } logger.LogInfo("Created " + forwarders.Count.ToString(CultureInfo.InvariantCulture) + " TCP Connection Forwarders:\r\n" + logSb.ToString()); // Run the forwarders. List <Task> forwarderTasks = new List <Task>(); foreach (KeyValuePair <long, TcpConnectionForwarder> kvp in forwarders) { TcpConnectionForwarder forwarder = kvp.Value; Task t = forwarder.RunAsync(); if (t.IsCompleted) { // The task has already completed without an async wait - this should mean there was some exception. try { await t; // Await the task to catch an exception (e.g. thrown by the TcpListener). } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } logger.LogError("Error when starting forwarder with ID " + kvp.Key.ToString(CultureInfo.InvariantCulture) + ":\r\n" + FormatExceptionForLog(ex)); } } else { // Wrap the task so that exceptions immediately lead to the process termination. Task newt = new Func <Task>(async() => await ExceptionUtils.WrapTaskForHandlingUnhandledExceptions(async() => await t))(); forwarderTasks.Add(newt); } } // Await the forwarder tasks. foreach (Task t in forwarderTasks) { await t; } }
private static List <ForwarderConfiguration> GetConfiguration(IBackgroundLogger errorLogger) { List <ForwarderConfiguration> configs = new List <ForwarderConfiguration>(); try { // Read the XML configuration file. XDocument doc; using (FileStream fs = new FileStream(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "configuration.xml"), FileMode.Open, FileAccess.Read)) { doc = XDocument.Load(fs); } XElement root = doc.Root; if (root.Name.LocalName == "TcpForwarderConfiguration") { foreach (XNode node in root.Nodes()) { try { if (node is XElement && ((XElement)node).Name.LocalName == "Forwarder") { // Found an entry. XElement el = (XElement)node; XAttribute remoteHost = el.Attribute("remoteHost"); XAttribute remotePort = el.Attribute("remotePort"); XAttribute localPort = el.Attribute("localPort"); if (remoteHost == null || remotePort == null || localPort == null) { throw new ArgumentException("One of the required attributes (maxConcurrentConnections, remoteHost, remotePort, localPort) is missing."); } int maxConcurrentConnections; XAttribute maxConcurrentConnectionsAttr = el.Attribute("maxConcurrentConnections"); if (maxConcurrentConnectionsAttr != null) { maxConcurrentConnections = int.Parse(maxConcurrentConnectionsAttr.Value, CultureInfo.InvariantCulture); } else { maxConcurrentConnections = 10000; } string remoteSslHostname = null; XAttribute at = el.Attribute("remoteSslHostname"); if (at != null) { remoteSslHostname = at.Value; } X509Certificate2 cert = null; at = el.Attribute("localSslCertFingerprint"); if (at != null) { cert = TcpConnectionForwarderUtils.GetCurrentUserOrLocalMachineCertificateFromFingerprint(at.Value); if (cert == null) { throw new Exception("The certificate with fingerprint \"" + at.Value + "\" was not found."); } // check if we have enough privileges to get the private key of the certificate (when using the local machine's store, // the program might need administrative rights to access the private key) try { System.Security.Cryptography.AsymmetricAlgorithm am = cert.PrivateKey; GC.KeepAlive(am); // Ensure the compiler does not optimize-away the above call } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } throw new Exception("Error when retrieving the private key of the certificate. Make sure " + "you run this program with correct privileges.\r\n\r\n" + ex.GetType().ToString() + ": " + ex.Message, ex); } } ForwarderConfiguration conf = new ForwarderConfiguration() { MaxConcurrentConnections = maxConcurrentConnections, RemoteHost = remoteHost.Value, RemotePort = ushort.Parse(remotePort.Value, CultureInfo.InvariantCulture), LocalPort = ushort.Parse(localPort.Value, CultureInfo.InvariantCulture), RemoteSslHostname = remoteSslHostname, LocalSslCert = cert }; configs.Add(conf); } } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } errorLogger.LogError("An error occured when parsing the following XML configuration entry:\r\n" + node.ToString() + "\r\n\r\nError Details:\r\n" + FormatExceptionForLog(ex)); } } } } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } errorLogger.LogError("An error when reading the XML configuration.\r\n\r\nError Details:\r\n" + FormatExceptionForLog(ex)); } return(configs); }