/// <summary> /// Log an event or exception. /// </summary> /// <param name="LogWriter">Log writer object used for documenting events.</param> /// <param name="sessionId">The current session's unique ID.</param> /// <param name="connectionId">The current connection's unique ID.</param> /// <param name="message">The message to log.</param> /// <param name="minimalLogLevel">The minimal log level needed for the message to be logged.</param> /// <param name="currentLogLevel">The session's current log level.</param> public static void Log(FreyaStreamWriter LogWriter, string sessionId, string connectionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) { if ((int)currentLogLevel >= (int)minimalLogLevel) { try { if (LogWriter != null) { if (LogWriter.textLogEn) { lock (LogWriter) { LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t" + connectionId + "\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); LogWriter.Flush(); } } LogWriter.radioSend(connectionId + " " + message + (message.EndsWith("\r\n") ? "" : "\r\n")); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("[" + DateTime.Now + "]\t" + sessionId + "\t" + connectionId + "\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); } }
/// <summary> /// Log an event or exception. /// </summary> /// <param name="LogWriter">Log writer object used for documenting events.</param> /// <param name="sessionId">The current session's unique ID.</param> /// <param name="message">The message to log.</param> /// <param name="minimalLogLevel">The minimal log level needed for the message to be logged.</param> /// <param name="currentLogLevel">The session's current log level.</param> public static void Log(FreyaStreamWriter LogWriter, string sessionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) { if ((int)currentLogLevel >= (int)minimalLogLevel) { if (LogWriter != null) { if (LogWriter.textLogEn) { lock (LogWriter) { LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); LogWriter.Flush(); } } if (!LogWriter.radioSend(message + (message.EndsWith("\r\n") ? "" : "\r\n")).Equals(FEnv.RADIO_OK)) { LogWriter.Write($"Error sending message to UI. SMTP Radio UI port :{LogWriter?.radioClient?.GetPort()}"); } } Console.WriteLine("[" + DateTime.Now + "]\t" + sessionId + "\t\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); } }
/// <summary> /// Start a POP3 proxy instance. /// </summary> /// <param name="acceptedIPs">IP addresses to accept connections from.</param> /// <param name="localIPAddress">Local IP address to bind to.</param> /// <param name="localPort">Local port to listen on.</param> /// <param name="localEnableSsl">Whether the local server supports TLS/SSL.</param> /// <param name="remoteServerHostName">Remote server hostname to forward all POP3 messages to.</param> /// <param name="remoteServerPort">Remote server port to connect to.</param> /// <param name="remoteServerEnableSsl">Whether the remote POP3 server requires TLS/SSL.</param> /// <param name="remoteServerCredential">(Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally.</param> /// <param name="exportDirectory">(Optional) Location where all incoming messages are saved as EML files.</param> /// <param name="logFile">File where event logs and exception information will be written.</param> /// <param name="logLevel">Proxy logging level, determining how much information is logged.</param> /// <param name="instanceId">The instance number of the proxy.</param> /// <param name="debugMode">Whether the proxy instance is running in DEBUG mode and should output full exception messages.</param> public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string exportDirectory, string logFile, LogLevel logLevel, int instanceId, bool debugMode) { // Create the log writer. string logFileName = ""; if (!string.IsNullOrEmpty(logFile)) { logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); LogWriter = new FreyaStreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); LogWriter.AutoFlush = true; LogLevel = logLevel; } // Make sure the remote server isn't an infinite loop back to this server. string fqdn = Functions.GetLocalFQDN(); if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) { ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); return; } IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); foreach (IPAddress hostIP in hostEntry.AddressList) { if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) { ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); return; } } ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); // Attempt to start up to 3 times in case another service using the port is shutting down. int startAttempts = 0; while (startAttempts < 3) { startAttempts++; // If we've failed to start once, wait an extra 10 seconds. if (startAttempts > 1) { ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); Thread.Sleep(10000 * startAttempts); } try { X509Certificate serverCertificate = null; // Generate a unique session ID for logging. SessionId = Guid.NewGuid().ToString(); ConnectionId = 0; // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. if (localEnableSsl) { serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); // In case the service as running as the current user, check the Current User certificate store as well. if (serverCertificate == null) { serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); } // If no certificate was found, generate a self-signed certificate. if (serverCertificate == null) { ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); List <DerObjectIdentifier> oids = new List <DerObjectIdentifier>(); oids.Add(new DerObjectIdentifier("1.3.6.1.5.5.7.3.1")); // Server Authentication. // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, StoreLocation.LocalMachine, true, 4096, 10, oids); ProxyFunctions.Log(LogWriter, SessionId, "Certificate generated with Serial Number {" + serverCertificate.GetSerialNumberString() + "}.", Proxy.LogLevel.Information, LogLevel); } } // Start listening on the specified port and IP address. Listener = new TcpListener(localIPAddress, localPort); Listener.Start(); ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); Started = true; // Accept client requests, forking each into its own thread. while (Started) { TcpClient client = Listener.AcceptTcpClient(); string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); if (newLogFileName != logFileName) { if (LogWriter != null) { LogWriter.Close(); } LogWriter = new FreyaStreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); LogWriter.AutoFlush = true; } // Prepare the arguments for our new thread. Pop3ProxyConnectionArguments arguments = new Pop3ProxyConnectionArguments(); arguments.AcceptedIPs = acceptedIPs; arguments.TcpClient = client; arguments.Certificate = serverCertificate; arguments.ExportDirectory = exportDirectory; arguments.LocalIpAddress = localIPAddress; arguments.LocalPort = localPort; arguments.LocalEnableSsl = localEnableSsl; arguments.RemoteServerHostName = remoteServerHostName; arguments.RemoteServerPort = remoteServerPort; arguments.RemoteServerEnableSsl = remoteServerEnableSsl; arguments.RemoteServerCredential = remoteServerCredential; // Increment the connection counter; arguments.ConnectionId = (unchecked (++ConnectionId)).ToString(); arguments.InstanceId = instanceId; arguments.DebugMode = debugMode; // Fork the thread and continue listening for new connections. Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); processThread.Name = "OpaqueMail POP3 Proxy Connection"; processThread.Start(arguments); } return; } catch (Exception ex) { if (debugMode || System.Diagnostics.Debugger.IsAttached) { ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.ToString(), Proxy.LogLevel.Critical, LogLevel); } else { ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); } } } }