Exemple #1
0
        protected override void OnStart(string[] args)
        {
            RabbitMqInboundStagingHandler.Start();

            Logging.Info("Starting Manta MTA Service.");

            AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e)
            {
                Exception ex = (Exception)e.ExceptionObject;
                Logging.Fatal(ex.Message, ex);
            };

            // Start the RabbitMQ Bulk inserter.
            QueueManager.Instance.Start();

            VirtualMTACollection ipAddresses = VirtualMtaManager.GetVirtualMtasForListeningOn();

            // Create the SmtpServers
            for (int c = 0; c < ipAddresses.Count; c++)
            {
                VirtualMTA ipAddress = ipAddresses[c];
                for (int i = 0; i < MtaParameters.ServerListeningPorts.Length; i++)
                {
                    _SmtpServers.Add(new SmtpServer(ipAddress.IPAddress, MtaParameters.ServerListeningPorts[i]));
                }
            }

            // Start the SMTP Client.
            MessageSender.Instance.Start();

            // Start the events (bounce/abuse) handler.
            EventsFileHandler.Instance.Start();

            Logging.Info("Manta MTA Service has started.");
        }
Exemple #2
0
        /// <summary>
        /// Save the specified Virtual MTA to the Database.
        /// </summary>
        /// <param name="vmta"></param>
        public static void Save(VirtualMTA vmta)
        {
            using (SqlConnection conn = MantaDB.GetSqlConnection())
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"
IF EXISTS(SELECT 1 FROM man_ip_ipAddress WHERE ip_ipAddress_id = @id)
	BEGIN
		UPDATE man_ip_ipAddress
		SET ip_ipAddress_ipAddress = @ipAddress,
			ip_ipAddress_hostname = @hostname,
			ip_ipAddress_isInbound = @isInbound,
			ip_ipAddress_isOutbound = @isOutbound
		WHERE ip_ipAddress_id = @id
	END
ELSE
	BEGIN
		INSERT INTO man_ip_ipAddress(ip_ipAddress_ipAddress, ip_ipAddress_hostname, ip_ipAddress_isInbound, ip_ipAddress_isOutbound)
		VALUES(@ipAddress, @hostname, @isInbound, @isOutbound)
	END
";
                cmd.Parameters.AddWithValue("@id", vmta.ID);
                cmd.Parameters.AddWithValue("@ipAddress", vmta.IPAddress.ToString());
                cmd.Parameters.AddWithValue("@hostname", vmta.Hostname);
                cmd.Parameters.AddWithValue("@isInbound", vmta.IsSmtpInbound);
                cmd.Parameters.AddWithValue("@isOutbound", vmta.IsSmtpOutbound);
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
        /// <summary>
        /// Gets the maximum amount of simultaneous connections to specified host.
        /// </summary>
        /// <param name="ipAddress">IP Address connecting from.</param>
        /// <param name="record">MXRecord of the destination.</param>
        /// <returns>Max number of connections.</returns>
        internal static int GetMaxConnectionsToDestination(VirtualMTA ipAddress, MXRecord record)
        {
            int mxPatternID = -1;
            OutboundRuleCollection rules = OutboundRuleManager.GetRules(record, ipAddress, out mxPatternID);

            for (int i = 0; i < rules.Count; i++)
            {
                if (rules[i].Type == OutboundRuleType.MaxConnections)
                {
                    int tmp = 0;
                    if (int.TryParse(rules[i].Value, out tmp))
                    {
                        return(tmp);
                    }
                    else
                    {
                        Logging.Error("Failed to get max connections for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to 1");
                        return(1);
                    }
                }
            }

            Logging.Error("Failed to get max connections for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to 1");
            return(1);
        }
        /// <summary>
        /// Gets the MAX number of messages allowed to be sent through the connection.
        /// </summary>
        /// <param name="record">MX Record for the destination.</param>
        /// <param name="ipAddress">IPAddress that we are sending from.</param>
        /// <returns>Max number of messages per connection.</returns>
        public static int GetMaxMessagesPerConnection(MXRecord record, VirtualMTA ipAddress)
        {
            int mxPatternID = 0;
            OutboundRuleCollection rules = GetRules(record, ipAddress, out mxPatternID);

            for (int i = 0; i < rules.Count; i++)
            {
                if (rules[i].Type == OutboundRuleType.MaxMessagesConnection)
                {
                    int tmp = 0;
                    if (int.TryParse(rules[i].Value, out tmp))
                    {
                        return(tmp);
                    }
                    else
                    {
                        Logging.Error("Failed to get max messages per connection for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to 1");
                        return(1);
                    }
                }
            }

            Logging.Error("Failed to get max messages per connection for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to 1");
            return(1);
        }
Exemple #5
0
        /// <summary>
        /// Gets permission to attempt a send of a message.
        /// </summary>
        /// <param name="ipAddress">The IP Address we wan't to send from.</param>
        /// <param name="mxRecord">The MX Record of the destination.</param>
        /// <returns>TRUE if we can send FALSE if we should throttle.</returns>
        public bool TryGetSendAuth(VirtualMTA ipAddress, MXRecord mxRecord)
        {
            int mxPatternID     = -1;
            int maxMessagesHour = OutboundRuleManager.GetMaxMessagesDestinationHour(ipAddress, mxRecord, out mxPatternID);

            // If the Max messages is -1 then unlimited so can just return true here.
            // No need for any logging or calculating.
            if (maxMessagesHour == -1)
            {
                return(true);
            }

            // Create or get this outbound IP/mx pattern send history.
            ThrottleManager.MxPatternThrottlingSendHistory mxSndHist = null;
            if (!this._sendHistory.TryGetValue(ipAddress.IPAddress.ToString(), out mxSndHist))
            {
                mxSndHist = new ThrottleManager.MxPatternThrottlingSendHistory();
                this._sendHistory.AddOrUpdate(ipAddress.IPAddress.ToString(), mxSndHist, (string s, ThrottleManager.MxPatternThrottlingSendHistory sh) => mxSndHist);
            }
            //this._sendHistory.GetOrAdd(ipAddress.IPAddress.ToString(), new ThrottleManager.MxPatternThrottlingSendHistory());
            List <DateTime> sndHistory = mxSndHist.GetOrAdd(mxPatternID, new List <DateTime>());

            // Only calculate if needed.
            if (mxSndHist.IntervalValuesNeedRecalcTimestamp <= DateTime.UtcNow)
            {
                int maxMessages = 0;
                int maxMessagesIntervalMinute = 0;
                while (maxMessages < 1 && maxMessagesIntervalMinute <= 60)
                {
                    maxMessagesIntervalMinute++;
                    maxMessages = (int)Math.Floor((double)maxMessagesHour / 60d * (double)maxMessagesIntervalMinute);
                }

                mxSndHist.IntervalMaxMessages = maxMessages;
                mxSndHist.IntervalMinutes     = maxMessagesIntervalMinute;
                mxSndHist.IntervalValuesNeedRecalcTimestamp = DateTime.UtcNow.AddMinutes(MtaParameters.MTA_CACHE_MINUTES);
            }


            lock (sndHistory)
            {
                // Remove sends that happened over "Interval" minute(s) ago.
                DateTime sendsAfterTimestamp = DateTime.UtcNow.AddMinutes((double)(mxSndHist.IntervalMinutes * -1));
                sndHistory.RemoveAll((DateTime d) => d <= sendsAfterTimestamp);

                // Check for throttling
                if (sndHistory.Count < mxSndHist.IntervalMaxMessages)
                {
                    // Not hit throttle limit yet.
                    // Log send and return true.
                    sndHistory.Add(DateTime.UtcNow);
                    return(true);
                }
                else
                {
                    // THROTTLED!
                    return(false);
                }
            }
        }
            /// <summary>
            /// Adds or updates.
            /// </summary>
            /// <param name="mxPatternID">The matching pattern ID</param>
            /// <param name="ipAddress">IP Address if specific or NULL</param>
            public void Add(int mxPatternID, VirtualMTA ipAddress)
            {
                OutboundRuleManager.MatchedMxPattern newMxPattern = new OutboundRuleManager.MatchedMxPattern();
                newMxPattern.MatchedUtc  = DateTime.UtcNow;
                newMxPattern.MxPatternID = mxPatternID;

                Func <string, OutboundRuleManager.MatchedMxPattern, OutboundRuleManager.MatchedMxPattern> updateAction = delegate(string key, OutboundRuleManager.MatchedMxPattern existing)
                {
                    if (existing.MatchedUtc > newMxPattern.MatchedUtc)
                    {
                        return(existing);
                    }
                    return(newMxPattern);
                };

                if (ipAddress != null)
                {
                    newMxPattern.IPAddress = ipAddress.IPAddress.ToString();
                    this.AddOrUpdate(newMxPattern.IPAddress,
                                     new MatchedMxPattern()
                    {
                        MatchedUtc  = DateTime.UtcNow,
                        MxPatternID = mxPatternID
                    }, updateAction);
                }
                else
                {
                    this.AddOrUpdate(string.Empty,
                                     new OutboundRuleManager.MatchedMxPattern
                    {
                        MatchedUtc  = DateTime.UtcNow,
                        MxPatternID = mxPatternID
                    }, updateAction);
                }
            }
        /// <summary>
        ///
        /// </summary>
        /// <param name="vmta"></param>
        /// <param name="mxRecord"></param>
        /// <param name="maxConnections"></param>
        public MantaOutboundClientPool(VirtualMTA vmta, MXRecord mxRecord)
        {
            _LastUsedTimestamp = DateTime.UtcNow.Ticks;
            MXRecord           = mxRecord;
            VirtualMTA         = vmta;

            var maxMessagesHour = OutboundRuleManager.GetMaxMessagesDestinationHour(vmta, mxRecord);

            if (maxMessagesHour > 0)
            {
                MaxMessagesMinute = (int?)Math.Floor(maxMessagesHour / 60d);
                SentMessagesLog   = new List <long>();
                Logging.Debug("MantaOutboundClientPool> for: " + vmta.IPAddress + "-" + mxRecord.Host + " MAX MESSAGES MIN: " + MaxMessagesMinute);
            }
            else
            {
                MaxMessagesMinute = null;
                SentMessagesLog   = null;
            }

            var maxConnections = OutboundRuleManager.GetMaxConnectionsToDestination(vmta, mxRecord);

            if (maxConnections > 0)
            {
                MaxConnections = maxConnections;
                Logging.Debug("MantaOutboundClientPool> for: " + vmta.IPAddress + "-" + mxRecord.Host + " MAX CONNECTION: " + MaxConnections);
            }
            else
            {
                MaxConnections = null;
            }

            SmtpClients = new List <MantaOutboundClient>();
        }
Exemple #8
0
 public MantaOutboundClientSendResult(MantaOutboundClientResult result, string message, VirtualMTA vmta = null, MXRecord mx = null)
 {
     MantaOutboundClientResult = result;
     Message    = message;
     VirtualMTA = vmta;
     MXRecord   = mx;
 }
 /// <summary>
 /// Creates a SmtpOutboundClient bound to the specified endpoint.
 /// </summary>
 /// <param name="ipAddress">The local IP address to bind to.</param>
 public SmtpOutboundClient(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;
 }
Exemple #10
0
        /// <summary>
        /// Creates a VirtualMTA object filled with the values from the DataRecord.
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        private static VirtualMTA CreateAndFillVirtualMtaFromRecord(IDataRecord record)
        {
            VirtualMTA vmta = new 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);
        }
Exemple #11
0
        static void Main(string[] args)
        {
            Logging.Info("MTA Started");

            AppDomain.CurrentDomain.FirstChanceException += delegate(object sender, FirstChanceExceptionEventArgs e)
            {
                Logging.Warn("", e.Exception);
            };

            IList <VirtualMTA> ipAddresses = VirtualMtaManager.GetVirtualMtasForListeningOn();

            // Array will hold all instances of SmtpServer, one for each port we will be listening on.
            List <SmtpServer> smtpServers = new List <SmtpServer>();

            // Create the SmtpServers
            for (int c = 0; c < ipAddresses.Count; c++)
            {
                VirtualMTA ipAddress = ipAddresses[c];
                for (int i = 0; i < MtaParameters.ServerListeningPorts.Length; i++)
                {
                    smtpServers.Add(new SmtpServer(ipAddress.IPAddress, MtaParameters.ServerListeningPorts[i]));
                }
            }

            // Start the SMTP Client.
            MessageSender.Instance.Start();
            // Start the events (bounce/abuse) handler.
            EventsFileHandler.Instance.Start();

            QueueManager.Instance.Start();
            OpenManta.Framework.RabbitMq.RabbitMqInboundStagingHandler.Start();

            bool quit = false;

            while (!quit)
            {
                ConsoleKeyInfo key = System.Console.ReadKey(true);
                if (key.KeyChar == 'q' || key.KeyChar == 'Q')
                {
                    quit = true;
                }
            }

            // Need to wait while servers & client shutdown.
            MantaCoreEvents.InvokeMantaCoreStopping();
            foreach (SmtpServer s in smtpServers)
            {
                s.Dispose();
            }

            Logging.Info("MTA Stopped");
            System.Console.WriteLine("Press any key to continue");
            System.Console.ReadKey(true);
        }
Exemple #12
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="vmta"></param>
        /// <param name="mxRecord"></param>
        private void AddClientPool(VirtualMTA vmta, MXRecord mxRecord)
        {
            var key = GetPoolKey(vmta, mxRecord);

            lock (AddClientPoolLock)
            {
                if (!ClientPools.ContainsKey(key))
                {
                    ClientPools[key] = new MantaOutboundClientPool(vmta, mxRecord);
                }
            }
        }
Exemple #13
0
        /// <summary>
        /// Creates a SmtpOutboundClient bound to the specified endpoint.
        /// </summary>
        /// <param name="ipAddress">The local IP address to bind to.</param>
        public MantaOutboundClient(VirtualMTA vmta, MXRecord mx) : base()
        {
            _VirtualMta = vmta;
            _MXRecord   = mx;
            TcpClient   = CreateTcpClient();

            _CanPipeline = false;

            _MaxMessagesConnection = OutboundRuleManager.GetMaxMessagesPerConnection(mx, vmta);
            if (_MaxMessagesConnection < 1)
            {
                _MaxMessagesConnection = null;
            }
        }
Exemple #14
0
        /// <summary>
        /// Gets the Outbound Rules for the specified destination MX and optionally IP Address.
        /// </summary>
        /// <param name="mxRecord">MXRecord for the destination MX.</param>
        /// <param name="mtaIpAddress">Outbound IP Address</param>
        /// <param name="mxPatternID">OUT: the ID of MxPattern that caused match.</param>
        /// <returns></returns>
        public static IList <OutboundRule> GetRules(MXRecord mxRecord, VirtualMTA mtaIpAddress, out int mxPatternID)
        {
            // Get the data from the database. This needs to be cleverer and reload every x minutes.
            if (OutboundRuleManager._MXPatterns == null)
            {
                OutboundRuleManager._MXPatterns = OutboundRuleDB.GetOutboundRulePatterns();
            }
            if (OutboundRuleManager._Rules == null)
            {
                OutboundRuleManager._Rules = OutboundRuleDB.GetOutboundRules();
            }

            int patternID = OutboundRuleManager.GetMxPatternID(mxRecord, mtaIpAddress);

            mxPatternID = patternID;
            return((from r in OutboundRuleManager._Rules
                    where r.OutboundMxPatternID == patternID
                    select r).ToList());
        }
        public bool Save(SaveVirtualMtaViewModel viewModel)
        {
            VirtualMTA vMTA = null;

            if (viewModel.Id != WebInterfaceParameters.VIRTUALMTA_NEW_ID)
            {
                vMTA = VirtualMtaDB.GetVirtualMta(viewModel.Id);
            }
            else
            {
                vMTA = new VirtualMTA();
            }

            if (vMTA == null)
            {
                return(false);
            }

            if (string.IsNullOrWhiteSpace(viewModel.HostName))
            {
                return(false);
            }

            IPAddress ip = null;

            try
            {
                ip = IPAddress.Parse(viewModel.IpAddress);
            }
            catch (Exception)
            {
                return(false);
            }

            vMTA.Hostname       = viewModel.HostName;
            vMTA.IPAddress      = ip;
            vMTA.IsSmtpInbound  = viewModel.Inbound;
            vMTA.IsSmtpOutbound = viewModel.Outbound;
            OpenManta.WebLib.DAL.VirtualMtaDB.Save(vMTA);
            return(true);
        }
        public bool Save(int id, string hostname, string ipAddress, bool inbound, bool outbound)
        {
            VirtualMTA vMTA = null;

            if (id != WebInterfaceParameters.VIRTUALMTA_NEW_ID)
            {
                vMTA = MantaMTA.Core.DAL.VirtualMtaDB.GetVirtualMta(id);
            }
            else
            {
                vMTA = new VirtualMTA();
            }

            if (vMTA == null)
            {
                return(false);
            }

            if (string.IsNullOrWhiteSpace(hostname))
            {
                return(false);
            }

            IPAddress ip = null;

            try
            {
                ip = IPAddress.Parse(ipAddress);
            }
            catch (Exception)
            {
                return(false);
            }

            vMTA.Hostname       = hostname;
            vMTA.IPAddress      = ip;
            vMTA.IsSmtpInbound  = inbound;
            vMTA.IsSmtpOutbound = outbound;
            VirtualMtaDB.Save(vMTA);
            return(true);
        }
Exemple #17
0
            /// <summary>
            /// Gets the matched MX Record. Null if not found.
            /// </summary>
            /// <param name="ipAddress"></param>
            /// <returns></returns>
            public OutboundRuleManager.MatchedMxPattern GetMatchedMxPattern(VirtualMTA ipAddress)
            {
                OutboundRuleManager.MatchedMxPattern tmp;
                if (base.TryGetValue(ipAddress.IPAddress.ToString(), out tmp))
                {
                    if (tmp.MatchedUtc.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) < DateTime.UtcNow)
                    {
                        return(tmp);
                    }
                }
                else
                {
                    if (base.TryGetValue(string.Empty, out tmp))
                    {
                        if (tmp.MatchedUtc.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) > DateTime.UtcNow)
                        {
                            return(tmp);
                        }
                    }
                }

                return(null);
            }
Exemple #18
0
        /// <summary>
        /// Gets the maximum amount of messages to send per hour from each ip address to mx.
        /// </summary>
        /// <param name="ipAddress">Outbound IP address</param>
        /// <param name="record">MX Record of destination server.</param>
        /// <param name="mxPatternID">ID of the pattern used to identify the rule.</param>
        /// <returns>Maximum number of messages per hour or -1 for unlimited.</returns>
        public static int GetMaxMessagesDestinationHour(VirtualMTA ipAddress, MXRecord record, out int mxPatternID)
        {
            OutboundRuleCollection rules = OutboundRuleManager.GetRules(record, ipAddress, out mxPatternID);

            for (int i = 0; i < rules.Count; i++)
            {
                if (rules[i].Type == OutboundRuleType.MaxMessagesPerHour)
                {
                    int tmp = 0;
                    if (int.TryParse(rules[i].Value, out tmp))
                    {
                        return(tmp);
                    }
                    else
                    {
                        Logging.Error("Failed to get max messages per hour for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to unlimited");
                        return(-1);
                    }
                }
            }

            Logging.Error("Failed to get max messages per hour for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to unlimited");
            return(-1);
        }
        public bool SaveGroup(int id, string name, string description, int[] mtaIDs)
        {
            VirtualMtaGroup grp = null;

            if (id == WebInterfaceParameters.VIRTUALMTAGROUP_NEW_ID)
            {
                grp = new VirtualMtaGroup();
            }
            else
            {
                grp = MantaMTA.Core.DAL.VirtualMtaGroupDB.GetVirtualMtaGroup(id);
            }

            if (grp == null)
            {
                return(false);
            }

            grp.Name        = name;
            grp.Description = description;

            VirtualMTACollection vMtas = MantaMTA.Core.DAL.VirtualMtaDB.GetVirtualMtas();

            for (int i = 0; i < mtaIDs.Length; i++)
            {
                VirtualMTA mta = vMtas.SingleOrDefault(m => m.ID == mtaIDs[i]);
                if (mta == null)
                {
                    return(false);
                }
                grp.VirtualMtaCollection.Add(mta);
            }

            VirtualMtaWebManager.Save(grp);
            return(true);
        }
        public bool SaveGroup(SaveVirtualMtaGroupViewModel viewModel)
        {
            VirtualMtaGroup grp = null;

            if (viewModel.Id == WebInterfaceParameters.VIRTUALMTAGROUP_NEW_ID)
            {
                grp = new VirtualMtaGroup();
            }
            else
            {
                grp = VirtualMtaGroupDB.GetVirtualMtaGroup(viewModel.Id);
            }

            if (grp == null)
            {
                return(false);
            }

            grp.Name        = viewModel.Name;
            grp.Description = viewModel.Description;

            var vMtas = VirtualMtaDB.GetVirtualMtas();

            for (int i = 0; i < viewModel.MtaIDs.Length; i++)
            {
                VirtualMTA mta = vMtas.SingleOrDefault(m => m.ID == viewModel.MtaIDs[i]);
                if (mta == null)
                {
                    return(false);
                }
                grp.VirtualMtaCollection.Add(mta);
            }

            VirtualMtaWebManager.Save(grp);
            return(true);
        }
Exemple #21
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 static async Task <bool> HandleServiceUnavailableAsync(MtaQueuedMessage msg, VirtualMTA ipAddress)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Deferred, "Service Unavailable", ipAddress, null);

            // Set next retry time and release the lock.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddSeconds(15);
            await Requeue(msg);

            return(true);
        }
Exemple #22
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 static async Task <bool> HandleDeliveryDeferralAsync(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false, int?overrideTimeminutes = null)
        {
            // Log the deferral.
            await MtaTransaction.LogTransactionAsync(msg, 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.
            msg.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 < msg.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.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(nextRetryInterval);
            await Requeue(msg);

            return(true);
        }
Exemple #23
0
        /// <summary>
        /// Gets the MxPatternID that matches the MX Record, Outbound IP Address combo.
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        private static int GetMxPatternID(MXRecord record, VirtualMTA ipAddress)
        {
            if (_matchedPatterns == null)
            {
                _matchedPatterns = new ConcurrentDictionary <string, MatchedMxPatternCollection>();
            }

            MatchedMxPatternCollection matchedPatterns = null;

            if (!_matchedPatterns.TryGetValue(record.Host, out matchedPatterns))
            {
                matchedPatterns = new MatchedMxPatternCollection();
                _matchedPatterns.AddOrUpdate(record.Host, matchedPatterns, (string s, MatchedMxPatternCollection p) => matchedPatterns);
            }

            MatchedMxPattern matchedPattern = matchedPatterns.GetMatchedMxPattern(ipAddress);

            if (matchedPattern != null &&
                matchedPattern.MatchedUtc.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) > DateTime.UtcNow)
            {
                // Found a valid cached pattern ID so return it.
                return(matchedPattern.MxPatternID);
            }

            // Loop through all of the patterns
            for (int i = 0; i < _MXPatterns.Count; i++)
            {
                // The current pattern we're working with.
                OutboundMxPattern pattern = _MXPatterns[i];

                // If the pattern applies only to a specified IP address then
                // only check for a match if getting rules for that IP.
                if (pattern.LimitedToOutboundIpAddressID.HasValue &&
                    pattern.LimitedToOutboundIpAddressID.Value != ipAddress.ID)
                {
                    continue;
                }

                if (pattern.Type == OutboundMxPatternType.CommaDelimited)
                {
                    // Pattern is a comma delimited list, so split the values
                    string[] strings = pattern.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    // Loop though the values in the split string array.
                    for (int c = 0; c < strings.Length; c++)
                    {
                        try
                        {
                            // If they are a match return the rules.
                            if (strings[c].Equals(record.Host, StringComparison.OrdinalIgnoreCase))
                            {
                                if (pattern.LimitedToOutboundIpAddressID.HasValue)
                                {
                                    matchedPatterns.Add(pattern.ID, ipAddress);
                                }
                                else
                                {
                                    matchedPatterns.Add(pattern.ID, null);
                                }

                                return(pattern.ID);
                            }
                        }
                        catch (Exception) { }
                    }

                    continue;
                }
                else if (pattern.Type == OutboundMxPatternType.Regex)
                {
                    // Pattern is Regex so just need to do an IsMatch
                    if (Regex.IsMatch(record.Host, pattern.Value, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture))
                    {
                        // Found pattern match.
                        if (pattern.LimitedToOutboundIpAddressID.HasValue)
                        {
                            matchedPatterns.Add(pattern.ID, ipAddress);
                        }
                        else
                        {
                            matchedPatterns.Add(pattern.ID, null);
                        }

                        return(pattern.ID);
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    // Don't know what to do with this pattern so move on to the next.
                    Logging.Error("Unknown OutboundMxPatternType : " + pattern.Type.ToString());
                    continue;
                }
            }

            // Should have been found by default at least, but hasn't.
            Logging.Fatal("No MX Pattern Rules! Default Deleted?");
            MantaCoreEvents.InvokeMantaCoreStopping();
            Environment.Exit(0);
            return(-1);
        }
Exemple #24
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 static void HandleDeliveryDeferral(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false)
 {
     HandleDeliveryDeferralAsync(msg, defMsg, ipAddress, mxRecord, isServiceUnavailable).Wait();
 }
Exemple #25
0
        /// <summary>
        /// This method handles message throttle.
        ///	Logs throttle
        /// Sets the next rety date time
        /// </summary>
        internal static async Task <bool> HandleDeliveryThrottleAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Throttled, string.Empty, ipAddress, mxRecord);

            // Set next retry time and release the lock.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
            msg.RabbitMqPriority    = RabbitMqPriority.Low;
            await Requeue(msg);

            return(true);
        }
Exemple #26
0
        public static int GetMaxMessagesDestinationHour(VirtualMTA vmta, MXRecord mx)
        {
            int tmp;

            return(GetMaxMessagesDestinationHour(vmta, mx, out tmp));
        }
Exemple #27
0
        /// <summary>
        /// This method handles failure of delivery.
        /// Logs failure
        /// Deletes queued data
        /// </summary>
        /// <param name="failMsg"></param>
        public static async Task <bool> HandleDeliveryFailAsync(MtaQueuedMessage msg, string failMsg, VirtualMTA ipAddress, MXRecord mxRecord)
        {
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Failed, failMsg, ipAddress, mxRecord);

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

            msg.IsHandled = true;

            return(true);
        }
Exemple #28
0
 private string GetPoolKey(VirtualMTA vmta, MXRecord mxRecord)
 {
     return(vmta.ID.ToString() + "-" + mxRecord.Host.ToLower());
 }
Exemple #29
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="vmta"></param>
        /// <param name="mxRecord"></param>
        /// <param name="rawMsg"></param>
        /// <returns></returns>
        private async Task <MantaOutboundClientSendResult> SendAsync(MailAddress mailFrom, MailAddress rcptTo, VirtualMTA vmta, MXRecord mxRecord, string msg)
        {
            var key = GetPoolKey(vmta, mxRecord);

            if (!ClientPools.ContainsKey(key))
            {
                AddClientPool(vmta, mxRecord);
            }

            var pool = ClientPools[key];

            return(await pool.SendAsync(mailFrom, rcptTo, msg));
        }
Exemple #30
0
        /// <summary>
        /// Attempts to get a SmtpClient using the outbound IP address and the specified MX records collection.
        ///
        /// WARNING: returned SmtpOutboundClient will have it's IsActive flag set to true make sure to set it to
        ///		     false when done with it or it will never be removed by the idle timeout.
        /// </summary>
        /// <param name="ipAddress">The local outbound endpoint we wan't to use.</param>
        /// <param name="mxs">The MX records for the domain we wan't a client to connect to.</param>
        /// <returns>Tuple containing a DequeueAsyncResult and either an SmtpOutboundClient or Null if failed to dequeue.</returns>
        public async Task <SmtpOutboundClientDequeueResponse> DequeueAsync(VirtualMTA ipAddress, MXRecord[] mxs)
        {
            // If there aren't any remote mx records then we can't send
            if (mxs.Length < 1)
            {
                return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.NoMxRecords));
            }

            SmtpClientMxRecords mxConnections = this._OutboundConnections.GetOrAdd(ipAddress.IPAddress.ToString(), new SmtpClientMxRecords());
            SmtpOutboundClient  smtpClient    = null;

            // Check that we aren't being throttled.
            if (!ThrottleManager.Instance.TryGetSendAuth(ipAddress, mxs[0]))
            {
                return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.Throttled));
            }

            // Loop through all the MX Records.
            for (int i = 0; i < mxs.Length; i++)
            {
                try
                {
                    // To prevent us bombarding a server that is blocking us we will check the service not available manager
                    // to see if we can send to this MX, Max 1 message/minute, if we can't we won't.
                    // At the moment we stop to all MXs for a domain if one of them responds with service unavailable.
                    // This could be improved to allow others to continue, we should however if blocked on all MX's with
                    // lowest preference  not move on to the others.
                    if (ServiceNotAvailableManager.IsServiceUnavailable(ipAddress.IPAddress.ToString(), mxs[i].Host))
                    {
                        return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.ServiceUnavalible));
                    }

                    SmtpClientQueue clientQueue = null;
                    lock (this._ClientPoolLock)
                    {
                        if (!mxConnections.TryGetValue(mxs[i].Host, out clientQueue))
                        {
                            clientQueue = new SmtpClientQueue();
                            if (!mxConnections.TryAdd(mxs[i].Host, clientQueue))
                            {
                                return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.FailedToAddToSmtpClientQueue));
                            }
                        }
                    }
                    // Loop through the client queue and make sure we get one thats still connected.
                    // They may have idled out while waiting.
                    while (!clientQueue.IsEmpty)
                    {
                        if (clientQueue.TryDequeue(out smtpClient))
                        {
                            if (smtpClient.Connected)
                            {
                                clientQueue.InUseConnections.Add(smtpClient);
                                smtpClient.LastActive = DateTime.UtcNow;
                                smtpClient.IsActive   = true;
                                return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.Success, smtpClient));
                            }
                        }
                    }

                    // Nothing was in the queue or all queued items timed out.
                    CreateNewConnectionAsyncResult createNewConnectionResult = await clientQueue.CreateNewConnectionAsync(ipAddress, mxs[i]);

                    if (createNewConnectionResult.Exception == null)
                    {
                        return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.Success, createNewConnectionResult.OutboundClient));
                    }
                    else if (createNewConnectionResult.Exception is MaxConnectionsException)
                    {
                        return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.FailedMaxConnections));
                    }
                    else
                    {
                        throw createNewConnectionResult.Exception;
                    }
                }
                catch (SocketException ex)
                {
                    // We have failed to connect to the remote host.
                    Logging.Warn("Failed to connect to " + mxs[i].Host, ex);

                    // If we fail to connect to an MX then don't try again for at least a minute.
                    ServiceNotAvailableManager.Add(ipAddress.IPAddress.ToString(), mxs[i].Host, DateTime.UtcNow);

                    // Failed to connect to MX
                    if (i == mxs.Length - 1)
                    {
                        return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.FailedToConnect));
                    }
                }
                catch (Exception ex)
                {
                    // Something unexpected and unhandled has happened, log it and return unknown.
                    Logging.Error("SmtpClientPool.DequeueAsync Unhandled Exception", ex);
                    return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.Unknown));
                }
            }

            // It we got here then we have failed to connect to the remote host.
            return(new SmtpOutboundClientDequeueResponse(SmtpOutboundClientDequeueAsyncResult.FailedToConnect));
        }