Exemple #1
0
        /// <summary>
        /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption.
        /// </summary>
        /// <param name="o">A ProcessMessageArguments object containing message parameters.</param>
        private void ProcessMessage(object o)
        {
            ProcessMessageArguments arguments = (ProcessMessageArguments)o;

            // Export the message to a local directory.
            if (!string.IsNullOrEmpty(arguments.ExportDirectory))
            {
                string messageId = Functions.ReturnBetween(arguments.MessageText.ToLower(), "message-id: <", ">");
                if (string.IsNullOrEmpty(messageId))
                {
                    messageId = Guid.NewGuid().ToString();
                }

                string fileName = ProxyFunctions.GetExportFileName(arguments.ExportDirectory, messageId, arguments.InstanceId, arguments.UserName);
                File.WriteAllText(fileName, arguments.MessageText);
            }

            // Only parse the message if it contains a known S/MIME content type.
            string canonicalMessageText = arguments.MessageText.ToLower();

            if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1)
            {
                try
                {
                    // Parse the message.
                    MailMessage message = new MailMessage(arguments.MessageText);

                    // If the message contains a signing certificate that we haven't processed on this session, import it.
                    foreach (X509Certificate2 cert in message.SmimeSigningCertificateChain)
                    {
                        if (cert != null && !SmimeCertificatesReceived.Contains(cert))
                        {
                            // Import the certificate to the Local Machine store.
                            ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + cert.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel);
                            CertHelper.InstallWindowsCertificate(cert, StoreLocation.LocalMachine);

                            // Remember this ceriticate to avoid importing it again this session.
                            SmimeCertificatesReceived.Add(cert);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                    {
                        ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                    }
                    else
                    {
                        ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
                    }
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Relay data read from one connection to another.
        /// </summary>
        /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param>
        private async void RelayData(object o)
        {
            // Cast the passed-in parameters back to their original objects.
            TransmitArguments arguments          = (TransmitArguments)o;
            Stream            clientStream       = arguments.ClientStream;
            Stream            remoteServerStream = arguments.RemoteServerStream;

            // A byte array to streamline bit shuffling.
            char[] buffer = new char[Constants.SMALLBUFFERSIZE];

            // Placeholder variables to track the current message being transmitted.
            StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // The overall number of bytes transmitted on this connection.
            ulong bytesTransmitted = 0;

            if (arguments.Credential != null)
            {
                UserName = arguments.Credential.UserName;
            }

            bool stillReceiving = true;

            try
            {
                using (StreamReader clientStreamReader = new StreamReader(clientStream))
                {
                    using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream))
                    {
                        remoteServerStreamWriter.AutoFlush = true;

                        while (Started && stillReceiving)
                        {
                            // Read data from the source and send it to its destination.
                            string stringRead = await clientStreamReader.ReadLineAsync();

                            if (stringRead != null)
                            {
                                int bytesRead = stringRead.Length;
                                bytesTransmitted += (ulong)bytesRead;

                                messageBuilder.AppendLine(stringRead);

                                // If this data comes from the client, log it.  Otherwise, process it.
                                if (arguments.IsClient)
                                {
                                    bool messageRelayed = false;

                                    string[] commandParts = stringRead.Substring(0, stringRead.Length).Split(new char[] { ' ' }, 2);

                                    // Optionally replace credentials with those from our settings file.
                                    if (commandParts.Length == 2)
                                    {
                                        if (commandParts[0] == "USER")
                                        {
                                            if (arguments.Credential != null)
                                            {
                                                await remoteServerStreamWriter.WriteLineAsync("USER " + arguments.Credential.UserName);

                                                ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel);

                                                messageRelayed = true;
                                            }
                                            else
                                            {
                                                UserName = commandParts[1];
                                            }
                                        }
                                        else if (arguments.Credential != null && commandParts[0] == "PASS")
                                        {
                                            await remoteServerStreamWriter.WriteLineAsync("PASS" + arguments.Credential.Password);

                                            ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel);

                                            messageRelayed = true;
                                        }
                                    }

                                    if (LogLevel == Proxy.LogLevel.Verbose)
                                    {
                                        ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel);
                                    }

                                    if (!messageRelayed)
                                    {
                                        await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                        ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel);
                                    }
                                }
                                else
                                {
                                    await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel);

                                    // If we see a period between two linebreaks, treat it as the end of a message.
                                    if (stringRead == ".")
                                    {
                                        string message = messageBuilder.ToString();
                                        int    endPos  = message.IndexOf("\r\n.\r\n");

                                        if (message.Contains("\r\n\r\n"))
                                        {
                                            int lastOkPos = message.LastIndexOf("+OK", endPos);
                                            if (lastOkPos > -1)
                                            {
                                                message = message.Substring(message.IndexOf("\r\n", lastOkPos) + 2);
                                                if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory))
                                                {
                                                    Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage));
                                                    processThread.Name = "OpaqueMail POP3 Proxy Signature Processor";
                                                    ProcessMessageArguments processMessageArguments = new ProcessMessageArguments();
                                                    processMessageArguments.MessageText     = message.Substring(0, message.Length - 5);
                                                    processMessageArguments.ConnectionId    = ConnectionId.ToString();
                                                    processMessageArguments.ExportDirectory = arguments.ExportDirectory;
                                                    processMessageArguments.InstanceId      = arguments.InstanceId;
                                                    processMessageArguments.DebugMode       = arguments.DebugMode;
                                                    processMessageArguments.UserName        = UserName;
                                                    processThread.Start(processMessageArguments);
                                                }
                                                messageBuilder.Remove(0, endPos + 5);
                                            }
                                        }

                                        messageBuilder.Clear();
                                    }
                                }
                            }
                            else
                            {
                                stillReceiving = false;
                            }
                        }
                    }
                }
            }
            catch (IOException)
            {
                // Ignore either stream being closed.
            }
            catch (ObjectDisposedException)
            {
                // Ignore either stream being closed.
            }
            catch (Exception ex)
            {
                // Log other exceptions.
                if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                }
                else
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
                }
            }
            finally
            {
                // If sending to the local client, log the connection being closed.
                if (!arguments.IsClient)
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel);
                }

                if (clientStream != null)
                {
                    clientStream.Dispose();
                }
                if (remoteServerStream != null)
                {
                    remoteServerStream.Dispose();
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Relay data read from one connection to another.
        /// </summary>
        /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param>
        private async void RelayData(object o)
        {
            // Cast the passed-in parameters back to their original objects.
            TransmitArguments arguments = (TransmitArguments)o;
            Stream clientStream = arguments.ClientStream;
            Stream remoteServerStream = arguments.RemoteServerStream;

            // A byte array to streamline bit shuffling.
            char[] buffer = new char[Constants.SMALLBUFFERSIZE];

            // Placeholder variables to track the current message being transmitted.
            StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // The overall number of bytes transmitted on this connection.
            ulong bytesTransmitted = 0;

            if (arguments.Credential != null)
                UserName = arguments.Credential.UserName;

            bool stillReceiving = true;
            try
            {
                using (StreamReader clientStreamReader = new StreamReader(clientStream))
                {
                    using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream))
                    {
                        remoteServerStreamWriter.AutoFlush = true;

                        while (Started && stillReceiving)
                        {
                            // Read data from the source and send it to its destination.
                            string stringRead = await clientStreamReader.ReadLineAsync();

                            if (stringRead != null)
                            {
                                int bytesRead = stringRead.Length;
                                bytesTransmitted += (ulong)bytesRead;

                                messageBuilder.AppendLine(stringRead);

                                // If this data comes from the client, log it.  Otherwise, process it.
                                if (arguments.IsClient)
                                {
                                    bool messageRelayed = false;

                                    string[] commandParts = stringRead.Substring(0, stringRead.Length).Split(new char[] { ' ' }, 2);

                                    // Optionally replace credentials with those from our settings file.
                                    if (commandParts.Length == 2)
                                    {
                                        if (commandParts[0] == "USER")
                                        {
                                            if (arguments.Credential != null)
                                            {
                                                await remoteServerStreamWriter.WriteLineAsync("USER " + arguments.Credential.UserName);

                                                ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel);

                                                messageRelayed = true;
                                            }
                                            else
                                                UserName = commandParts[1];
                                        }
                                        else if (arguments.Credential != null && commandParts[0] == "PASS")
                                        {
                                            await remoteServerStreamWriter.WriteLineAsync("PASS" + arguments.Credential.Password);

                                            ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel);

                                            messageRelayed = true;
                                        }
                                    }

                                    if (LogLevel == Proxy.LogLevel.Verbose)
                                        ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel);

                                    if (!messageRelayed)
                                    {
                                        await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                        ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel);
                                    }
                                }
                                else
                                {
                                    await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel);

                                    // If we see a period between two linebreaks, treat it as the end of a message.
                                    if (stringRead == ".")
                                    {
                                        string message = messageBuilder.ToString();
                                        int endPos = message.IndexOf("\r\n.\r\n");

                                        if (message.Contains("\r\n\r\n"))
                                        {
                                            int lastOkPos = message.LastIndexOf("+OK", endPos);
                                            if (lastOkPos > -1)
                                            {
                                                message = message.Substring(message.IndexOf("\r\n", lastOkPos) + 2);
                                                if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory))
                                                {
                                                    Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage));
                                                    processThread.Name = "OpaqueMail POP3 Proxy Signature Processor";
                                                    ProcessMessageArguments processMessageArguments = new ProcessMessageArguments();
                                                    processMessageArguments.MessageText = message.Substring(0, message.Length - 5);
                                                    processMessageArguments.ConnectionId = ConnectionId.ToString();
                                                    processMessageArguments.ExportDirectory = arguments.ExportDirectory;
                                                    processMessageArguments.InstanceId = arguments.InstanceId;
                                                    processMessageArguments.DebugMode = arguments.DebugMode;
                                                    processMessageArguments.UserName = UserName;
                                                    processThread.Start(processMessageArguments);
                                                }
                                                messageBuilder.Remove(0, endPos + 5);
                                            }
                                        }

                                        messageBuilder.Clear();
                                    }
                                }
                            }
                            else
                                stillReceiving = false;
                        }
                    }
                }
            }
            catch (IOException)
            {
                // Ignore either stream being closed.
            }
            catch (ObjectDisposedException)
            {
                // Ignore either stream being closed.
            }
            catch (Exception ex)
            {
                // Log other exceptions.
                if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                else
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
            }
            finally
            {
                // If sending to the local client, log the connection being closed.
                if (!arguments.IsClient)
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel);

                if (clientStream != null)
                    clientStream.Dispose();
                if (remoteServerStream != null)
                    remoteServerStream.Dispose();
            }
        }
Exemple #4
0
        /// <summary>
        /// Relay data read from one connection to another.
        /// </summary>
        /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param>
        private async void RelayData(object o)
        {
            // Cast the passed-in parameters back to their original objects.
            TransmitArguments arguments          = (TransmitArguments)o;
            Stream            clientStream       = arguments.ClientStream;
            Stream            remoteServerStream = arguments.RemoteServerStream;

            // A byte array to streamline bit shuffling.
            char[] buffer = new char[Constants.SMALLBUFFERSIZE];

            // Placeholder variables to track the current message being transmitted.
            bool          inMessage      = false;
            int           messageLength  = 0;
            StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // The overall number of bytes transmitted on this connection.
            ulong bytesTransmitted = 0;

            //  When a "[THROTTLED]" notice was last received.
            DateTime lastThrottleTime = new DateTime(1900, 1, 1);

            if (arguments.Credential != null)
            {
                UserName = arguments.Credential.UserName;
            }

            bool stillReceiving = true;

            try
            {
                using (StreamReader clientStreamReader = new StreamReader(clientStream))
                {
                    using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream))
                    {
                        remoteServerStreamWriter.AutoFlush = true;

                        while (Started && stillReceiving)
                        {
                            // Read data from the source and send it to its destination.
                            string stringRead = await clientStreamReader.ReadLineAsync();

                            if (stringRead != null)
                            {
                                int bytesRead = stringRead.Length;
                                bytesTransmitted += (ulong)bytesRead;

                                // If this data comes from the client, log it.  Otherwise, process it.
                                if (arguments.IsClient)
                                {
                                    bool messageRelayed = false;

                                    string[] commandParts = stringRead.Split(new char[] { ' ' }, 4);
                                    if (commandParts.Length > 1)
                                    {
                                        // Optionally, transform the login details.
                                        if (commandParts[1] == "LOGIN" && commandParts.Length == 4)
                                        {
                                            messageRelayed = TransformLogin(remoteServerStreamWriter, stringRead, arguments, ref UserName);
                                        }
                                        else
                                        {
                                            ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel);
                                        }

                                        // Remember the previous command.
                                        if (commandParts[1].ToUpper() == "UID" && commandParts.Length > 2)
                                        {
                                            LastCommandReceived = (commandParts[1] + " " + commandParts[2]).ToUpper();
                                        }
                                        else
                                        {
                                            LastCommandReceived = commandParts[1].ToUpper();
                                        }

                                        // Stop after a logout order is received.
                                        if (LastCommandReceived == "LOGOUT")
                                        {
                                            stillReceiving = false;
                                        }

                                        if (LogLevel == Proxy.LogLevel.Verbose)
                                        {
                                            switch (LastCommandReceived)
                                            {
                                            case "APPEND":
                                            case "AUTHENTICATE":
                                            case "CAPABILITY":
                                            case "CHECK":
                                            case "CLOSE":
                                            case "COPY":
                                            case "CREATE":
                                            case "DELETE":
                                            case "ENABLE":
                                            case "EXAMINE":
                                            case "EXPUNGE":
                                            case "FETCH":
                                            case "GETQUOTA":
                                            case "GETQUOTAROOT":
                                            case "LIST":
                                            case "LOGIN":
                                            case "LOGOUT":
                                            case "LSUB":
                                            case "MOVE":
                                            case "NOOP":
                                            case "NOTIFY":
                                            case "RENAME":
                                            case "SEARCH":
                                            case "SELECT":
                                            case "SETQUOTA":
                                            case "STATUS":
                                            case "STORE":
                                            case "SUBSCRIBE":
                                            case "UID COPY":
                                            case "UID FETCH":
                                            case "UID SEARCH":
                                            case "UID STORE":
                                            case "UNSUBSCRIBE":
                                            case "XLIST":
                                                ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel);
                                                break;
                                            }
                                        }
                                    }

                                    // If the command wasn't processed, send the raw command.
                                    if (!messageRelayed)
                                    {
                                        await remoteServerStreamWriter.WriteLineAsync(stringRead);
                                    }
                                }
                                else
                                {
                                    // If we're currently receiving a message, check to see if it's completed.
                                    if (inMessage)
                                    {
                                        messageBuilder.AppendLine(stringRead);
                                        if (messageBuilder.Length >= messageLength)
                                        {
                                            // If the message has been completed and it contains a signature, process it.
                                            string message = messageBuilder.ToString(0, messageLength);
                                            if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory))
                                            {
                                                Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage));
                                                processThread.Name = "OpaqueMail IMAP Proxy Signature Processor";
                                                ProcessMessageArguments processMessageArguments = new ProcessMessageArguments();
                                                processMessageArguments.MessageText     = message;
                                                processMessageArguments.ConnectionId    = ConnectionId.ToString();
                                                processMessageArguments.ExportDirectory = arguments.ExportDirectory;
                                                processMessageArguments.InstanceId      = arguments.InstanceId;
                                                processMessageArguments.DebugMode       = arguments.DebugMode;
                                                processMessageArguments.UserName        = UserName;
                                                processThread.Start(processMessageArguments);
                                            }

                                            // We're no longer receiving a message, so continue.
                                            inMessage = false;
                                            messageBuilder.Clear();
                                        }
                                    }
                                    else
                                    {
                                        // Disallow proxy stream compression.
                                        if (stringRead.StartsWith("* CAPABILITY"))
                                        {
                                            stringRead = stringRead.Replace(" COMPRESS=DEFLATE", "");
                                        }

                                        // Check for IMAP meta flags.
                                        string betweenBraces = Functions.ReturnBetween(stringRead, "[", "]");
                                        switch (betweenBraces)
                                        {
                                        case "CLIENTBUG":
                                        case "CORRUPTION":
                                        case "OVERQUOTA":
                                        case "SERVERBUG":
                                        case "UNAVAILABLE":
                                            ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), stringRead.Substring(stringRead.IndexOf("[")), Proxy.LogLevel.Warning, LogLevel);
                                            break;

                                        case "THROTTLED":
                                            if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0))
                                            {
                                                ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel);
                                                lastThrottleTime = DateTime.Now;
                                            }
                                            break;
                                        }

                                        // Messages are denoted by FETCH headers with their lengths in curly braces.
                                        if (stringRead.ToUpper().Contains(" FETCH "))
                                        {
                                            int openBrace = stringRead.IndexOf("{");
                                            if (openBrace > -1)
                                            {
                                                int closeBrace = stringRead.IndexOf("}", openBrace);
                                                if (closeBrace > -1)
                                                {
                                                    // Only proceed if we can parse the size of the message.
                                                    if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength))
                                                    {
                                                        inMessage = true;
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel);
                                }
                            }
                            else
                            {
                                stillReceiving = false;
                            }
                        }
                    }
                }
            }
            catch (IOException)
            {
                // Ignore either stream being closed.
            }
            catch (ObjectDisposedException)
            {
                // Ignore either stream being closed.
            }
            catch (Exception ex)
            {
                // Log other exceptions.
                if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                }
                else
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
                }
            }
            finally
            {
                // If sending to the local client, log the connection being closed.
                if (!arguments.IsClient)
                {
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel);
                }

                if (clientStream != null)
                {
                    clientStream.Dispose();
                }
                if (remoteServerStream != null)
                {
                    remoteServerStream.Dispose();
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Relay data read from one connection to another.
        /// </summary>
        /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param>
        private async void RelayData(object o)
        {
            // Cast the passed-in parameters back to their original objects.
            TransmitArguments arguments = (TransmitArguments)o;
            Stream clientStream = arguments.ClientStream;
            Stream remoteServerStream = arguments.RemoteServerStream;

            // A byte array to streamline bit shuffling.
            char[] buffer = new char[Constants.SMALLBUFFERSIZE];

            // Placeholder variables to track the current message being transmitted.
            bool inMessage = false;
            int messageLength = 0;
            StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // The overall number of bytes transmitted on this connection.
            ulong bytesTransmitted = 0;

            //  When a "[THROTTLED]" notice was last received.
            DateTime lastThrottleTime = new DateTime(1900, 1, 1);

            if (arguments.Credential != null)
                UserName = arguments.Credential.UserName;

            bool stillReceiving = true;
            try
            {
                using (StreamReader clientStreamReader = new StreamReader(clientStream))
                {
                    using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream))
                    {
                        remoteServerStreamWriter.AutoFlush = true;

                        while (Started && stillReceiving)
                        {
                            // Read data from the source and send it to its destination.
                            string stringRead = await clientStreamReader.ReadLineAsync();

                            if (stringRead != null)
                            {
                                int bytesRead = stringRead.Length;
                                bytesTransmitted += (ulong)bytesRead;

                                // If this data comes from the client, log it.  Otherwise, process it.
                                if (arguments.IsClient)
                                {
                                    bool messageRelayed = false;

                                    string[] commandParts = stringRead.Split(new char[] { ' ' }, 4);
                                    if (commandParts.Length > 1)
                                    {
                                        // Optionally replace credentials with those from our settings file.
                                        if (commandParts[1] == "LOGIN" && commandParts.Length == 4)
                                        {
                                            if (arguments.Credential != null)
                                            {
                                                await remoteServerStreamWriter.WriteLineAsync(commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password);

                                                ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel);

                                                messageRelayed = true;
                                            }
                                            else
                                                UserName = commandParts[2].Replace("\"", "");
                                        }
                                        else
                                            ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel);

                                        if (commandParts[1].ToUpper() == "UID" && commandParts.Length > 2)
                                            LastCommandReceived = (commandParts[1] + " " + commandParts[2]).ToUpper();
                                        else
                                            LastCommandReceived = commandParts[1].ToUpper();

                                        if (LastCommandReceived == "LOGOUT")
                                            stillReceiving = false;

                                        if (LogLevel == Proxy.LogLevel.Verbose)
                                        {
                                            switch (LastCommandReceived)
                                            {
                                                case "APPEND":
                                                case "AUTHENTICATE":
                                                case "CAPABILITY":
                                                case "CHECK":
                                                case "CLOSE":
                                                case "COPY":
                                                case "CREATE":
                                                case "DELETE":
                                                case "ENABLE":
                                                case "EXAMINE":
                                                case "EXPUNGE":
                                                case "FETCH":
                                                case "GETQUOTA":
                                                case "GETQUOTAROOT":
                                                case "LIST":
                                                case "LOGIN":
                                                case "LOGOUT":
                                                case "LSUB":
                                                case "MOVE":
                                                case "NOOP":
                                                case "NOTIFY":
                                                case "RENAME":
                                                case "SEARCH":
                                                case "SELECT":
                                                case "SETQUOTA":
                                                case "STATUS":
                                                case "STORE":
                                                case "SUBSCRIBE":
                                                case "UID COPY":
                                                case "UID FETCH":
                                                case "UID SEARCH":
                                                case "UID STORE":
                                                case "UNSUBSCRIBE":
                                                case "XLIST":
                                                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel);
                                                    break;
                                            }
                                        }
                                    }

                                    if (!messageRelayed)
                                        await remoteServerStreamWriter.WriteLineAsync(stringRead);
                                }
                                else
                                {
                                    // If we're currently receiving a message, check to see if it's completed.
                                    if (inMessage)
                                    {
                                        messageBuilder.AppendLine(stringRead);
                                        if (messageBuilder.Length >= messageLength)
                                        {
                                            // If the message has been completed and it contains a signature, process it.
                                            string message = messageBuilder.ToString(0, messageLength);
                                            if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory))
                                            {
                                                Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage));
                                                processThread.Name = "OpaqueMail IMAP Proxy Signature Processor";
                                                ProcessMessageArguments processMessageArguments = new ProcessMessageArguments();
                                                processMessageArguments.MessageText = message;
                                                processMessageArguments.ConnectionId = ConnectionId.ToString();
                                                processMessageArguments.ExportDirectory = arguments.ExportDirectory;
                                                processMessageArguments.InstanceId = arguments.InstanceId;
                                                processMessageArguments.DebugMode = arguments.DebugMode;
                                                processMessageArguments.UserName = UserName;
                                                processThread.Start(processMessageArguments);
                                            }

                                            // We're no longer receiving a message, so continue.
                                            inMessage = false;
                                            messageBuilder.Clear();
                                        }
                                    }
                                    else
                                    {
                                        // Disallow proxy stream compression.
                                        if (stringRead.StartsWith("* CAPABILITY"))
                                            stringRead = stringRead.Replace(" COMPRESS=DEFLATE", "");

                                        string betweenBraces = Functions.ReturnBetween(stringRead, "[", "]");
                                        switch (betweenBraces)
                                        {
                                            case "CLIENTBUG":
                                            case "CORRUPTION":
                                            case "OVERQUOTA":
                                            case "SERVERBUG":
                                            case "UNAVAILABLE":
                                                ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), stringRead.Substring(stringRead.IndexOf("[")), Proxy.LogLevel.Warning, LogLevel);
                                                break;
                                            case "THROTTLED":
                                                if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0))
                                                {
                                                    ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel);
                                                    lastThrottleTime = DateTime.Now;
                                                }
                                                break;
                                        }

                                        // Messages are denoted by FETCH headers with their lengths in curly braces.
                                        if (stringRead.ToUpper().Contains(" FETCH "))
                                        {
                                            int openBrace = stringRead.IndexOf("{");
                                            if (openBrace > -1)
                                            {
                                                int closeBrace = stringRead.IndexOf("}", openBrace);
                                                if (closeBrace > -1)
                                                {
                                                    // Only proceed if we can parse the size of the message.
                                                    if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength))
                                                        inMessage = true;
                                                }
                                            }
                                        }
                                    }

                                    await remoteServerStreamWriter.WriteLineAsync(stringRead);

                                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel);
                                }
                            }
                            else
                                stillReceiving = false;
                        }
                    }
                }
            }
            catch (IOException)
            {
                // Ignore either stream being closed.
            }
            catch (ObjectDisposedException)
            {
                // Ignore either stream being closed.
            }
            catch (Exception ex)
            {
                // Log other exceptions.
                if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                else
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
            }
            finally
            {
                // If sending to the local client, log the connection being closed.
                if (!arguments.IsClient)
                    ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel);

                if (clientStream != null)
                    clientStream.Dispose();
                if (remoteServerStream != null)
                    remoteServerStream.Dispose();
            }
        }