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."); }
/// <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); }
/// <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>(); }
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; }
/// <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); }
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); }
/// <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); } } }
/// <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; } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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(); }
/// <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); }
public static int GetMaxMessagesDestinationHour(VirtualMTA vmta, MXRecord mx) { int tmp; return(GetMaxMessagesDestinationHour(vmta, mx, out tmp)); }
/// <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); }
private string GetPoolKey(VirtualMTA vmta, MXRecord mxRecord) { return(vmta.ID.ToString() + "-" + mxRecord.Host.ToLower()); }
/// <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)); }
/// <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)); }