コード例 #1
0
        /// <summary>
        /// Handles processing the message once the end of the DATA SMTP stream is sent.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="eodArgs"></param>
        public void OnEndOfDataHandler(ReceiveEventSource source, EndOfDataEventArgs eodArgs)
        {
            Byte[]            newlineNeedle      = Encoding.ASCII.GetBytes("\n");
            Byte[]            scoreNeedle        = Encoding.ASCII.GetBytes("X-Spam-Score: ");
            Byte[]            discardFlag        = Encoding.ASCII.GetBytes("X-Spam-Discard: YES\n");
            Byte[]            recievedNeedle     = Encoding.ASCII.GetBytes("Received: ");
            Byte[]            indentedLineNeedle = Encoding.ASCII.GetBytes(" ");
            Nullable <Double> score = null;

            this.logger.debug("OnEndOfDataHandler Called");

            // Wrap everything in a Try. This makes sure that even if something fails the message still passes through
            try
            {
                this.logger.info("OnEndOfDataHandler Info: FROM=" + eodArgs.MailItem.FromAddress.ToString() + ", REMOTE=" + eodArgs.SmtpSession.RemoteEndPoint.Address.ToString());

                // Check to make sure SpamAssassin exists at the path it's supposed to
                if (!System.IO.File.Exists(this.settings.SpamassassinPath))
                {
                    this.logger.fatal("Spamassassin does not exist at path: '" + this.settings.SpamassassinPath + "'. Bypassing.");
                    this.logger.flush();
                    return;
                }

                // Check to see if this is an internal message
                if (!eodArgs.SmtpSession.IsExternalConnection)
                {
                    foreach (EnvelopeRecipient recipient in eodArgs.MailItem.Recipients)
                    {
                        if (recipient.Address.LocalPart.IndexOf("HealthMailbox") == 0)
                        {
                            this.logger.info("Health Mailbox found in recipients. Bypassing.");
                            this.logger.flush();
                        }
                    }
                    this.logger.info("Internal Connection Found. Bypassing.");
                    this.logger.flush();
                }

                // Is the message too big?
                if (eodArgs.MailItem.MimeStreamLength > this.settings.MaxMessageSize)
                {
                    this.logger.warning("Message is too large. Increase MaxMessageSize to scan larger messages. MAXSIZE=" + this.settings.MaxMessageSize.ToString() + ", MESSAGESIZE=" + eodArgs.MailItem.MimeStreamLength.ToString());
                    this.logger.flush();
                }

                // Get the message stream
                Stream      message          = eodArgs.MailItem.GetMimeReadStream();
                List <Byte> messageBytes     = new List <Byte>(ReadFully(message));
                byte[]      messageByteArray = messageBytes.ToArray();
                message.Close();
                this.logger.debug("Message Stream Retrieved. BYTES=" + messageByteArray.Length.ToString());

                // Skip the top number of recieved lines
                int linestart      = 0;
                int lineend        = 0;
                int linestartmatch = 0;
                for (int i = 0; i < this.settings.SkipRecieved; i++)
                {
                    this.logger.debug("Skipping #" + i.ToString() + " Recieved line");
                    // Find the first instance of the Recieved header
                    linestart = ByteSearch.Locate(messageByteArray, recievedNeedle, 0);
                    // Loop until we find a line that doesn't start with a space.
                    do
                    {
                        lineend = ByteSearch.Locate(messageByteArray, newlineNeedle, linestart);
                        this.logger.debug("Located Line End: " + lineend.ToString());
                        linestartmatch = ByteSearch.Locate(messageByteArray, indentedLineNeedle, lineend);
                        this.logger.debug("Located LineStartMatch:" + linestartmatch.ToString());
                        messageBytes.RemoveRange(linestart, lineend - linestart + 1);
                        messageByteArray = messageBytes.ToArray();
                    } while (linestartmatch == lineend + 1);

                    this.logger.debug("Finished Skipping #" + i.ToString());
                }

                // Run spamassassin while piping stdin/stdout
                this.logger.debug("Starting SpamAssassin Process. PATH='" + this.settings.SpamassassinPath + "', ARGS='" + this.settings.SpamassassinArgs + "'");
                Process spamassassin = new Process();
                spamassassin.StartInfo.UseShellExecute        = false;
                spamassassin.StartInfo.FileName               = this.settings.SpamassassinPath;
                spamassassin.StartInfo.WorkingDirectory       = Path.GetDirectoryName(spamassassin.StartInfo.FileName);
                spamassassin.StartInfo.Arguments              = ("-s " + this.settings.MaxMessageSize + " " + this.settings.SpamassassinArgs).Trim();
                spamassassin.StartInfo.RedirectStandardInput  = true;
                spamassassin.StartInfo.RedirectStandardOutput = true;
                spamassassin.StartInfo.RedirectStandardError  = true;
                spamassassin.Start();
                this.logger.debug("Started SpamAssassin Process. PID='" + spamassassin.Id.ToString() + "'");

                // Copy the message into stdio using a byte for byte copy
                this.logger.debug("Copying message to Spamassassin. BYTES=" + messageByteArray.Length.ToString());
                spamassassin.StandardInput.BaseStream.Write(messageByteArray, 0, messageByteArray.Length);
                spamassassin.StandardInput.BaseStream.Flush();
                spamassassin.StandardInput.BaseStream.Close();

                // Read the entire output buffer, put it into a list for easy manipulation
                List <Byte> outBytes = new List <Byte>(ReadFully(spamassassin.StandardOutput.BaseStream));
                this.logger.debug("Read STDOUT from spamassassin. BYTES=" + outBytes.Count.ToString());
                spamassassin.StandardOutput.BaseStream.Close();

                // Read the entire stderr buffer, place it in a file if there's anything to it
                Byte[] outErrorBytes = ReadFully(spamassassin.StandardError.BaseStream);
                this.logger.debug("Read STDERR from spamassassin. BYTES=" + outErrorBytes.Length.ToString());
                if (outErrorBytes.Length > 0)
                {
                    this.logger.error("Error From SpamAssassin: " + outErrorBytes.ToString());
                }
                spamassassin.StandardError.BaseStream.Close();

                // Wait for process to exit
                spamassassin.WaitForExit();

                // Find a header
                int flagStart  = ByteSearch.Locate(outBytes.ToArray(), scoreNeedle, 0);
                int scoreEnd   = -1;
                int scoreStart = 0;

                // Check if we found the start of the header
                if (flagStart > -1)
                {
                    scoreStart = flagStart + scoreNeedle.Length;
                    scoreEnd   = ByteSearch.Locate(outBytes.ToArray(), newlineNeedle, scoreStart);
                }
                else
                {
                    this.logger.warning("Could not find start of score in message.");
                }

                // Check if we found the end of the header
                if (scoreEnd > -1)
                {
                    Byte[] scoreBytes = outBytes.GetRange(scoreStart, scoreEnd - scoreStart).ToArray();
                    try
                    {
                        score = Double.Parse(new String(scoreBytes.Select(b => (Char)b).ToArray()).Trim());
                    }
                    catch (Exception e)
                    {
                        this.logger.warning("Could not parse score. Exception:" + e.Message);
                    }
                }
                else
                {
                    this.logger.warning("Could not find end of score in message.");
                }

                // If we found a score check it and process it
                if (score != null)
                {
                    if (score >= this.settings.RejectThreshold)
                    {
                        this.logger.info("Score(" + score.ToString() + ") above threshold(" + this.settings.RejectThreshold.ToString() + "), flagging for discard.");

                        outBytes.InsertRange(flagStart, discardFlag);
                    }
                    else
                    {
                        this.logger.info("Score(" + score.ToString() + ") below threshold(" + this.settings.RejectThreshold.ToString() + "), passing.");
                    }
                }

                // Write the message back to SpamAssassin
                Byte[] writeBytes = outBytes.ToArray();
                Stream messageOut = eodArgs.MailItem.GetMimeWriteStream();
                this.logger.debug("Writing message to MailItem. BYTES=" + writeBytes.Length.ToString());
                messageOut.Write(writeBytes, 0, writeBytes.Length);
                messageOut.Close();
            }
            catch (Exception e)
            {
                this.logger.fatal("Exception Detected");
                this.logger.fatal(e.ToString());
            }
            finally
            {
                this.logger.flush();
            }
        }
コード例 #2
0
        /// <summary>
        /// Handles the "RCPT TO:" SMTP command
        /// </summary>
        /// <param name="source">The event source.</param>
        /// <param name="eodArgs">The event arguments.</param>
        public void RcptToHandler(ReceiveEventSource source, RcptCommandEventArgs rcptArgs)
        {
            RoutingAddress catchAllAddress;

            // Check whether to handle the recipient's domain
            //if (this.catchAllConfig.AddressMap.TryGetValue(
            //    rcptArgs.RecipientAddress.DomainPart.ToLower(),
            //    out catchAllAddress))
            foreach (Regex r in this.catchAllConfig.AddressMap.Keys)
            {
                if(r.IsMatch(rcptArgs.RecipientAddress.ToString()))
                {
                    // DO THE DIRTY WORK HERE
                }
            }
            if (false)
            {
                // Rewrite the (envelope) recipient address if 'not found'
                if ((this.addressBook != null) &&
                    (this.addressBook.Find(rcptArgs.RecipientAddress) == null))
                {
                    rcptArgs.RecipientAddress = catchAllAddress;
                }
            }

            return;
        }
コード例 #3
0
        /// <summary>
        /// Handles processing the message once the end of the DATA SMTP stream is sent.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="eodArgs"></param>
        public void OnEndOfDataHandler(ReceiveEventSource source, EndOfDataEventArgs eodArgs)
        {
            Byte[] newlineNeedle = Encoding.ASCII.GetBytes("\n");
            Byte[] scoreNeedle = Encoding.ASCII.GetBytes("X-Spam-Score: ");
            Byte[] discardFlag = Encoding.ASCII.GetBytes("X-Spam-Discard: YES\n");
            Byte[] recievedNeedle = Encoding.ASCII.GetBytes("Received: ");
            Byte[] indentedLineNeedle = Encoding.ASCII.GetBytes(" ");
            Nullable<Double> score = null;

            this.logger.debug("OnEndOfDataHandler Called");

            // Wrap everything in a Try. This makes sure that even if something fails the message still passes through
            try
            {
                this.logger.info("OnEndOfDataHandler Info: FROM=" + eodArgs.MailItem.FromAddress.ToString() + ", REMOTE=" + eodArgs.SmtpSession.RemoteEndPoint.Address.ToString());

                // Check to make sure SpamAssassin exists at the path it's supposed to
                if (!System.IO.File.Exists(this.settings.SpamassassinPath))
                {
                    this.logger.fatal("Spamassassin does not exist at path: '" + this.settings.SpamassassinPath + "'. Bypassing.");
                    this.logger.flush();
                    return;
                }

                // Check to see if this is an internal message
                if (!eodArgs.SmtpSession.IsExternalConnection)
                {
                    foreach (EnvelopeRecipient recipient in eodArgs.MailItem.Recipients)
                    {
                        if (recipient.Address.LocalPart.IndexOf("HealthMailbox") == 0)
                        {
                            this.logger.info("Health Mailbox found in recipients. Bypassing.");
                            this.logger.flush();
                        }
                    }
                    this.logger.info("Internal Connection Found. Bypassing.");
                    this.logger.flush();
                }

                // Is the message too big?
                if (eodArgs.MailItem.MimeStreamLength > this.settings.MaxMessageSize)
                {
                    this.logger.warning("Message is too large. Increase MaxMessageSize to scan larger messages. MAXSIZE=" + this.settings.MaxMessageSize.ToString() + ", MESSAGESIZE=" + eodArgs.MailItem.MimeStreamLength.ToString());
                    this.logger.flush();
                }

                // Get the message stream
                Stream message = eodArgs.MailItem.GetMimeReadStream();
                List<Byte> messageBytes = new List<Byte>(ReadFully(message));
                byte[] messageByteArray = messageBytes.ToArray();
                message.Close();
                this.logger.debug("Message Stream Retrieved. BYTES=" + messageByteArray.Length.ToString());

                // Skip the top number of recieved lines
                int linestart = 0;
                int lineend = 0;
                int linestartmatch = 0;
                for (int i = 0; i < this.settings.SkipRecieved; i++)
                {
                    this.logger.debug("Skipping #" + i.ToString() + " Recieved line");
                    // Find the first instance of the Recieved header
                    linestart = ByteSearch.Locate(messageByteArray, recievedNeedle, 0);
                    // Loop until we find a line that doesn't start with a space.
                    do
                    {
                        lineend = ByteSearch.Locate(messageByteArray, newlineNeedle, linestart);
                        this.logger.debug("Located Line End: " + lineend.ToString());
                        linestartmatch = ByteSearch.Locate(messageByteArray, indentedLineNeedle, lineend);
                        this.logger.debug("Located LineStartMatch:" + linestartmatch.ToString());
                        messageBytes.RemoveRange(linestart, lineend - linestart + 1);
                        messageByteArray = messageBytes.ToArray();

                    } while (linestartmatch == lineend + 1);

                    this.logger.debug("Finished Skipping #" + i.ToString());

                }

                // Run spamassassin while piping stdin/stdout
                this.logger.debug("Starting SpamAssassin Process. PATH='" + this.settings.SpamassassinPath + "', ARGS='" + this.settings.SpamassassinArgs + "'");
                Process spamassassin = new Process();
                spamassassin.StartInfo.UseShellExecute = false;
                spamassassin.StartInfo.FileName = this.settings.SpamassassinPath;
                spamassassin.StartInfo.WorkingDirectory = Path.GetDirectoryName(spamassassin.StartInfo.FileName);
                spamassassin.StartInfo.Arguments = ("-s " + this.settings.MaxMessageSize + " " + this.settings.SpamassassinArgs).Trim();
                spamassassin.StartInfo.RedirectStandardInput = true;
                spamassassin.StartInfo.RedirectStandardOutput = true;
                spamassassin.StartInfo.RedirectStandardError = true;
                spamassassin.Start();
                this.logger.debug("Started SpamAssassin Process. PID='" + spamassassin.Id.ToString() + "'");

                // Copy the message into stdio using a byte for byte copy
                this.logger.debug("Copying message to Spamassassin. BYTES=" + messageByteArray.Length.ToString());
                spamassassin.StandardInput.BaseStream.Write(messageByteArray, 0, messageByteArray.Length);
                spamassassin.StandardInput.BaseStream.Flush();
                spamassassin.StandardInput.BaseStream.Close();

                // Read the entire output buffer, put it into a list for easy manipulation
                List<Byte> outBytes = new List<Byte>(ReadFully(spamassassin.StandardOutput.BaseStream));
                this.logger.debug("Read STDOUT from spamassassin. BYTES=" + outBytes.Count.ToString());
                spamassassin.StandardOutput.BaseStream.Close();

                // Read the entire stderr buffer, place it in a file if there's anything to it
                Byte[] outErrorBytes = ReadFully(spamassassin.StandardError.BaseStream);
                this.logger.debug("Read STDERR from spamassassin. BYTES=" + outErrorBytes.Length.ToString());
                if (outErrorBytes.Length > 0)
                {
                    this.logger.error("Error From SpamAssassin: " + outErrorBytes.ToString());
                }
                spamassassin.StandardError.BaseStream.Close();

                // Wait for process to exit
                spamassassin.WaitForExit();

                // Find a header
                int flagStart = ByteSearch.Locate(outBytes.ToArray(), scoreNeedle, 0);
                int scoreEnd = -1;
                int scoreStart = 0;

                // Check if we found the start of the header
                if (flagStart > -1)
                {
                    scoreStart = flagStart + scoreNeedle.Length;
                    scoreEnd = ByteSearch.Locate(outBytes.ToArray(), newlineNeedle, scoreStart);
                } else {
                    this.logger.warning("Could not find start of score in message.");
                }

                // Check if we found the end of the header
                if (scoreEnd > -1)
                {
                    Byte[] scoreBytes = outBytes.GetRange(scoreStart, scoreEnd - scoreStart).ToArray();
                    try
                    {
                        score = Double.Parse(new String(scoreBytes.Select(b => (Char)b).ToArray()).Trim());
                    }
                    catch(Exception e)
                    {
                        this.logger.warning("Could not parse score. Exception:" + e.Message);
                    }
                }
                else
                {
                    this.logger.warning("Could not find end of score in message.");
                }

                // If we found a score check it and process it
                if(score != null) {
                    if (score >= this.settings.RejectThreshold)
                    {
                        this.logger.info("Score(" + score.ToString() + ") above threshold(" + this.settings.RejectThreshold.ToString() + "), flagging for discard.");

                        outBytes.InsertRange(flagStart, discardFlag);
                    }
                    else
                    {
                        this.logger.info("Score(" + score.ToString() + ") below threshold(" + this.settings.RejectThreshold.ToString() + "), passing.");
                    }
                }

                // Write the message back to SpamAssassin
                Byte[] writeBytes = outBytes.ToArray();
                Stream messageOut = eodArgs.MailItem.GetMimeWriteStream();
                this.logger.debug("Writing message to MailItem. BYTES=" + writeBytes.Length.ToString());
                messageOut.Write(writeBytes, 0, writeBytes.Length);
                messageOut.Close();

            }
            catch (Exception e)
            {
                this.logger.fatal("Exception Detected");
                this.logger.fatal(e.ToString());
            }
            finally
            {
                this.logger.flush();
            }
            
        }