Example #1
0
        /// <summary>
        /// This method handle successful delivery.
        /// Logs success
        /// Deletes queued data
        /// </summary>
        public async Task <bool> HandleDeliverySuccessAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord, string response)
        {
            await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Success, response, ipAddress, mxRecord);

            IsHandled = true;
            return(true);
        }
Example #2
0
        /// <summary>
        /// Handles a service unavailable event, should be same as defer but only wait 1 minute before next retry.
        /// </summary>
        /// <param name="sndIpAddress"></param>
        internal async Task <bool> HandleServiceUnavailableAsync(VirtualMta.VirtualMTA ipAddress)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Deferred, "Service Unavailable", ipAddress, null);

            // Set next retry time and release the lock.
            this.AttemptSendAfterUtc = DateTime.UtcNow.AddSeconds(15);
            Requeue();
            return(true);
        }
Example #3
0
        /// <summary>
        /// This method handles message throttle.
        ///	Logs throttle
        /// Sets the next rety date time
        /// </summary>
        internal async Task <bool> HandleDeliveryThrottleAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Throttled, string.Empty, ipAddress, mxRecord);

            // Set next retry time and release the lock.
            this.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
            Requeue();
            return(true);
        }
Example #4
0
 /// <summary>
 /// Creates a VirtualMTA object filled with the values from the DataRecord.
 /// </summary>
 /// <param name="record"></param>
 /// <returns></returns>
 private static VirtualMta.VirtualMTA CreateAndFillVirtualMtaFromRecord(IDataRecord record)
 {
     VirtualMta.VirtualMTA vmta = new VirtualMta.VirtualMTA();
     vmta.ID             = record.GetInt32("ip_ipAddress_id");
     vmta.Hostname       = record.GetString("ip_ipAddress_hostname");
     vmta.IPAddress      = System.Net.IPAddress.Parse(record.GetString("ip_ipAddress_ipAddress"));
     vmta.IsSmtpInbound  = record.GetBoolean("ip_ipAddress_isInbound");
     vmta.IsSmtpOutbound = record.GetBoolean("ip_ipAddress_isOutbound");
     return(vmta);
 }
Example #5
0
 /// <summary>
 /// Creates a SmtpOutboundClient bound to the specified endpoint.
 /// </summary>
 /// <param name="ipAddress">The local IP address to bind to.</param>
 public SmtpOutboundClient(VirtualMta.VirtualMTA ipAddress) : base(new IPEndPoint(ipAddress.IPAddress, 0))
 {
     this.IsActive       = true;
     this.MtaIpAddress   = ipAddress;
     base.ReceiveTimeout = MtaParameters.Client.ConnectionReceiveTimeoutInterval * 1000;
     base.SendTimeout    = MtaParameters.Client.ConnectionSendTimeoutInterval * 1000;
     base.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
     SmtpOutboundClientCollection.Instance.Add(this);
     this.IsActive     = false;
     this._CanPipeline = false;
 }
Example #6
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="ipAddress"></param>
 /// <param name="mxRecord"></param>
 /// <returns></returns>
 public async Task <bool> HandleFailedToConnectAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
 {
     // If there was no MX record in DNS, so using A, we should fail and not retry.
     if (mxRecord.MxRecordSrc == DNS.MxRecordSrc.A)
     {
         return(await HandleDeliveryFailAsync("550 Failed to connect", ipAddress, mxRecord));
     }
     else
     {
         return(await HandleDeliveryDeferralAsync("Failed to connect", ipAddress, mxRecord, false, 15));
     }
 }
Example #7
0
        /// <summary>
        /// This method handles message deferal.
        ///	Logs deferral
        ///	Fails the message if timed out
        /// or
        /// Sets the next rety date time
        /// </summary>
        /// <param name="defMsg">The deferal message from the SMTP server.</param>
        /// <param name="ipAddress">IP Address that send was attempted from.</param>
        /// <param name="mxRecord">MX Record of the server tried to send too.</param>
        /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval,
        /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param>
        public async Task <bool> HandleDeliveryDeferralAsync(string defMsg, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord, bool isServiceUnavailable = false, int?overrideTimeminutes = null)
        {
            // Log the deferral.
            await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Deferred, defMsg, ipAddress, mxRecord);

            // This holds the maximum interval between send retries. Should be put in the database.
            int maxInterval = 3 * 60;

            // Increase the defered count as the queued messages has been deferred.
            DeferredCount++;

            // Hold the minutes to wait until next retry.
            double nextRetryInterval = MtaParameters.MtaRetryInterval;

            if (overrideTimeminutes.HasValue)
            {
                nextRetryInterval = overrideTimeminutes.Value;
            }
            else
            {
                if (!isServiceUnavailable)
                {
                    // Increase the deferred wait interval by doubling for each retry.
                    for (int i = 1; i < DeferredCount; i++)
                    {
                        nextRetryInterval = nextRetryInterval * 2;
                    }

                    // If we have gone over the max interval then set to the max interval value.
                    if (nextRetryInterval > maxInterval)
                    {
                        nextRetryInterval = maxInterval;
                    }
                }
                else
                {
                    nextRetryInterval = 1;                     // For service unavalible use 1 minute between retries.
                }
            }

            // Set next retry time and release the lock.
            this.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(nextRetryInterval);
            Requeue();

            return(true);
        }
Example #8
0
        public void SmtpClientMaxMessages()
        {
            using (SmtpServer s = new SmtpServer(25))
            {
                VirtualMta.VirtualMTA ipAddress = new VirtualMta.VirtualMTA()
                {
                    IPAddress = IPAddress.Parse("127.0.0.1")
                };
                MantaMTA.Core.DNS.MXRecord mxRecord = new MantaMTA.Core.DNS.MXRecord("localhost", 10, uint.MaxValue, DNS.MxRecordSrc.A);

                SmtpOutboundClient smtpClient = new SmtpOutboundClient(ipAddress);
                smtpClient.ConnectAsync(mxRecord).Wait();
                Assert.IsTrue(smtpClient.Connected);

                Action sendMessage = new Action(delegate()
                {
                    Action <string> callback = new Action <string>(delegate(string str) { });
                    Task.Run(async delegate()
                    {
                        await smtpClient.ExecHeloOrRsetAsync(callback);
                        await smtpClient.ExecMailFromAsync(new System.Net.Mail.MailAddress("testing@localhost"), callback);
                        await smtpClient.ExecRcptToAsync(new System.Net.Mail.MailAddress("postmaster@localhost"), callback);
                        await smtpClient.ExecDataAsync("hello", callback, async(response) => { await Task.Delay(100); });
                        return(true);
                    }).Wait();
                });

                sendMessage();
                Assert.IsTrue(smtpClient.Connected);

                sendMessage();
                Assert.IsTrue(smtpClient.Connected);

                sendMessage();
                Assert.IsTrue(smtpClient.Connected);

                sendMessage();
                Assert.IsTrue(smtpClient.Connected);

                sendMessage();
                Assert.IsFalse(smtpClient.Connected);
            }
        }
Example #9
0
        public void SmtpClientPoolTest()
        {
            using (SmtpServer s = new SmtpServer(25))
            {
                VirtualMta.VirtualMTA ipAddress = new VirtualMta.VirtualMTA()
                {
                    IPAddress = IPAddress.Parse("127.0.0.1")
                };
                MantaMTA.Core.DNS.MXRecord mxRecord = new MantaMTA.Core.DNS.MXRecord("localhost", 10, uint.MaxValue, DNS.MxRecordSrc.A);

                SmtpOutboundClient smtpClient = new SmtpOutboundClient(ipAddress);
                smtpClient.ConnectAsync(mxRecord).Wait();
                MantaMTA.Core.Smtp.SmtpClientPool.Instance.Enqueue(smtpClient);
                smtpClient = MantaMTA.Core.Smtp.SmtpClientPool.Instance.DequeueAsync(ipAddress, new MantaMTA.Core.DNS.MXRecord[] { mxRecord }).Result.SmtpOutboundClient;

                Assert.NotNull(smtpClient);
                Assert.IsTrue(smtpClient.Connected);
            }
        }
Example #10
0
        public void SmtpClientIdleTimeout()
        {
            if (MessageBox.Show("Do you want to run the idle timeout test?", "Idle timeout test", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                using (SmtpServer s = new SmtpServer(25))
                {
                    VirtualMta.VirtualMTA outboundEndpoint = new VirtualMta.VirtualMTA()
                    {
                        IPAddress = IPAddress.Parse("127.0.0.1")
                    };
                    MantaMTA.Core.DNS.MXRecord mxRecord = new MantaMTA.Core.DNS.MXRecord("localhost", 10, uint.MaxValue, DNS.MxRecordSrc.A);

                    SmtpOutboundClient smtpClient = new SmtpOutboundClient(outboundEndpoint);
                    smtpClient.ConnectAsync(mxRecord).Wait();

                    Assert.IsTrue(smtpClient.Connected);
                    System.Threading.Thread.Sleep((MantaMTA.Core.MtaParameters.Client.ConnectionIdleTimeoutInterval + 5) * 1000);
                    Assert.IsFalse(smtpClient.Connected);
                }
            }
        }
Example #11
0
        /// <summary>
        /// This method handles failure of delivery.
        /// Logs failure
        /// Deletes queued data
        /// </summary>
        /// <param name="failMsg"></param>
        public async Task <bool> HandleDeliveryFailAsync(string failMsg, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
        {
            await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Failed, failMsg, ipAddress, mxRecord);

            try
            {
                // Send fails to Manta.Core.Events
                for (int i = 0; i < base.RcptTo.Length; i++)
                {
                    EmailProcessingDetails processingInfo = null;
                    Events.EventsManager.Instance.ProcessSmtpResponseMessage(failMsg, base.RcptTo[i], base.InternalSendID, out processingInfo);
                }
            }
            catch (Exception)
            {
            }

            IsHandled = true;

            return(true);
        }
Example #12
0
        /// <summary>
        /// Attempt to create a new connection using the specified ip address and mx record.
        /// </summary>
        /// <returns>A connected outbound client or NULL</returns>
        public async Task <CreateNewConnectionAsyncResult> CreateNewConnectionAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
        {
            SmtpOutboundClient smtpClient = null;

            // Get the maximum connections to the destination.
            int maximumConnections = OutboundRules.OutboundRuleManager.GetMaxConnectionsToDestination(ipAddress, mxRecord);

            lock (this.SyncRoot)
            {
                // Get the currently active connections count.
                int currentConnections = this.InUseConnections.Count;

                lock (_ConnectionAttemptsInProgressLock)
                {
                    // If the current connections count + current connection is less than
                    // the maximum connections then we can create a new connection otherwise
                    // we are maxed out so return null.
                    if (maximumConnections <= (currentConnections + _ConnectionAttemptsInProgress))
                    {
                        return(new CreateNewConnectionAsyncResult(new MaxConnectionsException()));
                    }


                    // Limit the amount of connection attempts or experiance massive delays 30s+ for client.connect()
                    if (_ConnectionAttemptsInProgress >= SmtpClientQueue.MAX_SIMULTANEOUS_CLIENT_CONNECT_ATTEMPTS)
                    {
                        //Logging.Debug("Cannot attempt to create new connection.");
                        return(new CreateNewConnectionAsyncResult(new MaxConnectionsException()));
                    }

                    //Logging.Debug("Attempting to create new connection.");
                    _ConnectionAttemptsInProgress++;
                }
            }

            // Do the actual creating and connecting of the client outside of the lock
            // so we don't block other threads.

            try
            {
                // Create the new client and make the connection
                smtpClient = new SmtpOutboundClient(ipAddress);
                await smtpClient.ConnectAsync(mxRecord);

                smtpClient.IsActive = true;
                this.InUseConnections.Add(smtpClient);
            }
            catch (Exception ex)
            {
                // If something went wrong clear the client so we don't return something odd.
                if (smtpClient != null)
                {
                    smtpClient.Close();
                    smtpClient.Dispose();
                    smtpClient = null;
                }
                if (ex is SocketException)
                {
                    return(new CreateNewConnectionAsyncResult(ex));
                }

                if (ex is AggregateException && ex.InnerException is System.IO.IOException)
                {
                    return(new CreateNewConnectionAsyncResult(ex.InnerException));
                }
            }
            finally
            {
                // Reduce the current attempts as were done.
                _ConnectionAttemptsInProgress--;
                if (smtpClient != null)
                {
                    smtpClient.IsActive = false;
                }
            }

            // Return connected client or null.
            return(new CreateNewConnectionAsyncResult(smtpClient));
        }
Example #13
0
 /// <summary>
 /// This method handles message deferal.
 ///	Logs deferral
 ///	Fails the message if timed out
 /// or
 /// Sets the next rety date time
 /// </summary>
 /// <param name="defMsg">The deferal message from the SMTP server.</param>
 /// <param name="ipAddress">IP Address that send was attempted from.</param>
 /// <param name="mxRecord">MX Record of the server tried to send too.</param>
 /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval,
 /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param>
 public void HandleDeliveryDeferral(string defMsg, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord, bool isServiceUnavailable = false)
 {
     HandleDeliveryDeferralAsync(defMsg, ipAddress, mxRecord, isServiceUnavailable).Wait();
 }
Example #14
0
        /// <summary>
        /// Logs an MTA Transaction to the database.
        /// </summary>
        public static async Task <bool> LogTransactionAsync(MtaMessage msg, TransactionStatus status, string svrResponse, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
        {
            using (SqlConnection conn = MantaDB.GetSqlConnection())
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"
BEGIN TRANSACTION 
INSERT INTO man_mta_transaction (mta_msg_id, ip_ipAddress_id, mta_transaction_timestamp, mta_transactionStatus_id, mta_transaction_serverResponse, mta_transaction_serverHostname)
VALUES(@msgID, @ipAddressID, GETUTCDATE(), @status, @serverResponse, @serverHostname)";

                switch (status)
                {
                case TransactionStatus.Discarded:
                case TransactionStatus.Failed:
                case TransactionStatus.TimedOut:
                    cmd.CommandText += @"UPDATE man_mta_send
								SET mta_send_rejected = mta_send_rejected + 1
								WHERE mta_send_internalID = @sendInternalID"                                ;
                    break;

                case TransactionStatus.Success:
                    cmd.CommandText += @"UPDATE man_mta_send
								SET mta_send_accepted = mta_send_accepted + 1
								WHERE mta_send_internalID = @sendInternalID"                                ;
                    break;
                }

                cmd.CommandText += " COMMIT TRANSACTION";
                cmd.Parameters.AddWithValue("@sendInternalID", msg.InternalSendID);

                cmd.Parameters.AddWithValue("@msgID", msg.ID);
                if (ipAddress != null)
                {
                    cmd.Parameters.AddWithValue("@ipAddressID", ipAddress.ID);
                }
                else
                {
                    cmd.Parameters.AddWithValue("@ipAddressID", DBNull.Value);
                }

                if (mxRecord != null)
                {
                    cmd.Parameters.AddWithValue("@serverHostname", mxRecord.Host);
                }
                else
                {
                    cmd.Parameters.AddWithValue("@serverHostname", DBNull.Value);
                }

                cmd.Parameters.AddWithValue("@status", (int)status);
                cmd.Parameters.AddWithValue("@serverResponse", svrResponse);
                await conn.OpenAsync();

                await cmd.ExecuteNonQueryAsync();

                return(true);
            }
        }
Example #15
0
 /// <summary>
 /// Logs an MTA Transaction to the database.
 /// </summary>
 public static void LogTransaction(MtaMessage msg, TransactionStatus status, string svrResponse, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord)
 {
     LogTransactionAsync(msg, status, svrResponse, ipAddress, mxRecord).Wait();
 }