Пример #1
0
        /// <summary>
        /// Gets a single Virtual MTA Group.
        /// </summary>
        /// <param name="id">ID of the Virtual MTA Group to get.</param>
        /// <returns>The Virtual MTA Group or NULL if none exist with ID</returns>
        public static VirtualMtaGroup GetVirtualMtaGroup(int id)
        {
            VirtualMtaGroup grp = VirtualMtaGroupDB.GetVirtualMtaGroup(id);

            grp.VirtualMtaCollection = VirtualMtaDB.GetVirtualMtasInVirtualMtaGroup(grp.ID);
            return(grp);
        }
Пример #2
0
        /// <summary>
        /// Creates a MtaIPGroup object using the Data Record.
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        private static VirtualMtaGroup CreateAndFillVirtualMtaGroup(IDataRecord record)
        {
            VirtualMtaGroup group = new VirtualMtaGroup();

            group.ID   = record.GetInt32("ip_group_id");
            group.Name = record.GetString("ip_group_name");
            if (!record.IsDBNull("ip_group_description"))
            {
                group.Description = record.GetString("ip_group_description");
            }

            return(group);
        }
Пример #3
0
        /// <summary>
        /// Saves the virtual mta group to the database.
        /// </summary>
        /// <param name="grp">Group to save.</param>
        public static void Save(VirtualMtaGroup grp)
        {
            StringBuilder groupMembershipInserts = new StringBuilder();

            foreach (VirtualMTA vmta in grp.VirtualMtaCollection)
            {
                groupMembershipInserts.AppendFormat(@"{1}INSERT INTO man_ip_groupMembership(ip_group_id, ip_ipAddress_id)
VALUES(@id,{0}){1}", vmta.ID, Environment.NewLine);
            }

            using (SqlConnection conn = MantaDB.GetSqlConnection())
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"
BEGIN TRANSACTION

IF EXISTS(SELECT 1 FROM man_ip_group WHERE ip_group_id = @id)
	UPDATE man_ip_group
	SET ip_group_name = @name,
		ip_group_description = @description
	WHERE ip_group_id = @id
ELSE
	BEGIN
		INSERT INTO man_ip_group(ip_group_name, ip_group_description)
		VALUES(@name, @description)

		SELECT @id = @@IDENTITY
	END

DELETE 
FROM man_ip_groupMembership
WHERE ip_group_id = @id

" + groupMembershipInserts.ToString() + @"

COMMIT TRANSACTION";
                cmd.Parameters.AddWithValue("@id", grp.ID);
                cmd.Parameters.AddWithValue("@name", grp.Name);

                if (grp.Description == null)
                {
                    cmd.Parameters.AddWithValue("@description", DBNull.Value);
                }
                else
                {
                    cmd.Parameters.AddWithValue("@description", grp.Description);
                }
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
Пример #4
0
        /// <summary>
        /// Gets the specfied MTA IP Group
        /// </summary>
        /// <param name="id">ID of the group to get.</param>
        /// <returns>The IP Group or NULL if doesn't exist.</returns>
        public static VirtualMtaGroup GetVirtualMtaGroup(int id)
        {
            VirtualMtaGroup group = null;

            // Try and get IPGroup from the in memory collection.
            if (_vmtaGroups.TryGetValue(id, out group))
            {
                // Only cache IP Groups for N minutes.
                if (group.CreatedTimestamp.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) > DateTime.UtcNow)
                {
                    return(group);
                }
            }

            // We need to goto the database to get the group. Lock!
            lock (_MtaVirtualMtaGroupSyncLock)
            {
                // Check that something else didn't already load from the database.
                // If it did then we can just return that.
                _vmtaGroups.TryGetValue(id, out group);
                if (group != null && group.CreatedTimestamp.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) > DateTime.UtcNow)
                {
                    return(group);
                }

                // Get group from the database.
                group = VirtualMtaGroupDB.GetVirtualMtaGroup(id);

                // Group doesn't exist, so don't try and get it's IPs
                if (group == null)
                {
                    return(null);
                }

                // Got the group, go get it's IPs.
                group.VirtualMtaCollection = VirtualMtaDB.GetVirtualMtasInVirtualMtaGroup(id);

                // Add the group to collection, so others can use it.
                _vmtaGroups.AddOrUpdate(id, group, new Func <int, VirtualMtaGroup, VirtualMtaGroup>(delegate(int key, VirtualMtaGroup existing)
                {
                    return(group);
                }));
                return(group);
            }
        }
Пример #5
0
        //
        // GET: /VirtualMta/EditGroup
        public ActionResult EditGroup(int id = WebInterfaceParameters.VIRTUALMTAGROUP_NEW_ID)
        {
            VirtualMtaGroup grp = null;

            if (id == WebInterfaceParameters.VIRTUALMTAGROUP_NEW_ID)
            {
                grp = new VirtualMtaGroup();
            }
            else
            {
                grp = VirtualMtaWebManager.GetVirtualMtaGroup(id);
            }

            return(View(new VirtualMtaGroupCreateEditModel
            {
                VirtualMtaGroup = grp,
                VirtualMTACollection = VirtualMtaDB.GetVirtualMtas()
            }));
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="vMtaGroup"></param>
        /// <param name="mxRecord"></param>
        /// <param name="rawMsg"></param>
        /// <returns></returns>
        public async Task <MantaOutboundClientSendResult> SendAsync(MailAddress mailFrom, MailAddress rcptTo, VirtualMtaGroup vMtaGroup, MXRecord[] mxRecord, string msg)
        {
            Logging.Debug("MantaOutboundClientPoolCollection.SendAsync> From: " + mailFrom + " To:" + rcptTo + " via:" + vMtaGroup.Name);
            var result = null as MantaOutboundClientSendResult;

            foreach (var vmta in vMtaGroup.VirtualMtaCollection)
            {
                result = await SendAsync(mailFrom, rcptTo, vmta, mxRecord.OrderBy(mx => mx.Preference).First(), msg);

                switch (result.MantaOutboundClientResult)
                {
                case MantaOutboundClientResult.FailedToConnect:
                case MantaOutboundClientResult.RejectedByRemoteServer:
                case MantaOutboundClientResult.Success:
                    return(result);

                case MantaOutboundClientResult.ClientAlreadyInUse:
                case MantaOutboundClientResult.MaxConnections:
                case MantaOutboundClientResult.MaxMessages:
                case MantaOutboundClientResult.ServiceNotAvalible:
                    continue;
                }
            }

            return(result);
        }
Пример #9
0
 /// <summary>
 /// Saves the Virtual MTA Group.
 /// </summary>
 /// <param name="grp">Virtual MTA Group to save.</param>
 public static void Save(VirtualMtaGroup grp)
 {
     VirtualMtaGroupDB.Save(grp);
 }
Пример #10
0
        private async Task <bool> SendMessageAsync(MtaQueuedMessage msg)
        {
            // Check that the message next attempt after has passed.
            if (msg.AttemptSendAfterUtc > DateTime.UtcNow)
            {
                RabbitMqOutboundQueueManager.Enqueue(msg);
                await Task.Delay(50);                 // To prevent a tight loop within a Task thread we should sleep here.

                return(false);
            }

            if (await MtaTransaction.HasBeenHandledAsync(msg.ID))
            {
                msg.IsHandled = true;
                return(true);
            }

            // Get the send that this message belongs to so that we can check the send state.
            Send snd = await SendManager.Instance.GetSendAsync(msg.InternalSendID);

            switch (snd.SendStatus)
            {
            // The send is being discarded so we should discard the message.
            case SendStatus.Discard:
                await msg.HandleMessageDiscardAsync();

                return(false);

            // The send is paused, the handle pause state will delay, without deferring, the message for a while so we can move on to other messages.
            case SendStatus.Paused:
                msg.HandleSendPaused();
                return(false);

            // Send is active so we don't need to do anything.
            case SendStatus.Active:
                break;

            // Unknown send state, requeue the message and log error. Cannot send!
            default:
                msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
                RabbitMqOutboundQueueManager.Enqueue(msg);
                Logging.Error("Failed to send message. Unknown SendStatus[" + snd.SendStatus + "]!");
                return(false);
            }


            bool result;

            // Check the message hasn't timed out. If it has don't attempt to send it.
            // Need to do this here as there may be a massive backlog on the server
            // causing messages to be waiting for ages after there AttemptSendAfter
            // before picking up. The MAX_TIME_IN_QUEUE should always be enforced.
            if (msg.AttemptSendAfterUtc - msg.QueuedTimestampUtc > new TimeSpan(0, MtaParameters.MtaMaxTimeInQueue, 0))
            {
                await msg.HandleDeliveryFailAsync(MtaParameters.TIMED_OUT_IN_QUEUE_MESSAGE, null, null);

                result = false;
            }
            else
            {
                MailAddress mailAddress = new MailAddress(msg.RcptTo[0]);
                MailAddress mailFrom    = new MailAddress(msg.MailFrom);
                MXRecord[]  mXRecords   = DNSManager.GetMXRecords(mailAddress.Host);
                // If mxs is null then there are no MX records.
                if (mXRecords == null || mXRecords.Length < 1)
                {
                    await msg.HandleDeliveryFailAsync("550 Domain Not Found.", null, null);

                    result = false;
                }
                else if (IsMxBlacklisted(mXRecords))
                {
                    await msg.HandleDeliveryFailAsync("550 Domain blacklisted.", null, mXRecords[0]);

                    result = false;
                }
                else
                {
                    // The IP group that will be used to send the queued message.
                    VirtualMtaGroup virtualMtaGroup = VirtualMtaManager.GetVirtualMtaGroup(msg.VirtualMTAGroupID);
                    VirtualMTA      sndIpAddress    = virtualMtaGroup.GetVirtualMtasForSending(mXRecords[0]);

                    SmtpOutboundClientDequeueResponse dequeueResponse = await SmtpClientPool.Instance.DequeueAsync(sndIpAddress, mXRecords);

                    switch (dequeueResponse.DequeueResult)
                    {
                    case SmtpOutboundClientDequeueAsyncResult.Success:
                    case SmtpOutboundClientDequeueAsyncResult.NoMxRecords:
                    case SmtpOutboundClientDequeueAsyncResult.FailedToAddToSmtpClientQueue:
                    case SmtpOutboundClientDequeueAsyncResult.Unknown:
                        break;                                 // Don't need to do anything for these results.

                    case SmtpOutboundClientDequeueAsyncResult.FailedToConnect:
                        await msg.HandleFailedToConnectAsync(sndIpAddress, mXRecords[0]);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.ServiceUnavalible:
                        await msg.HandleServiceUnavailableAsync(sndIpAddress);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.Throttled:
                        await msg.HandleDeliveryThrottleAsync(sndIpAddress, mXRecords[0]);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.FailedMaxConnections:
                        msg.AttemptSendAfterUtc = DateTime.UtcNow.AddSeconds(2);
                        RabbitMqOutboundQueueManager.Enqueue(msg);
                        break;
                    }

                    SmtpOutboundClient smtpClient = dequeueResponse.SmtpOutboundClient;

                    // If no client was dequeued then we can't currently send.
                    // This is most likely a max connection issue. Return false but don't
                    // log any deferal or throttle.
                    if (smtpClient == null)
                    {
                        result = false;
                    }
                    else
                    {
                        try
                        {
                            Action <string> failedCallback = delegate(string smtpResponse)
                            {
                                // If smtpRespose starts with 5 then perm error should cause fail
                                if (smtpResponse.StartsWith("5"))
                                {
                                    msg.HandleDeliveryFailAsync(smtpResponse, sndIpAddress, smtpClient.MXRecord).Wait();
                                }
                                else
                                {
                                    // If the MX is actively denying use service access, SMTP code 421 then we should inform
                                    // the ServiceNotAvailableManager manager so it limits our attepts to this MX to 1/minute.
                                    if (smtpResponse.StartsWith("421"))
                                    {
                                        ServiceNotAvailableManager.Add(smtpClient.SmtpStream.LocalAddress.ToString(), smtpClient.MXRecord.Host, DateTime.UtcNow);
                                        msg.HandleDeliveryDeferral(smtpResponse, sndIpAddress, smtpClient.MXRecord, true);
                                    }
                                    else
                                    {
                                        // Otherwise message is deferred
                                        msg.HandleDeliveryDeferral(smtpResponse, sndIpAddress, smtpClient.MXRecord, false);
                                    }
                                }
                                throw new SmtpTransactionFailedException();
                            };
                            // Run each SMTP command after the last.
                            await smtpClient.ExecHeloOrRsetAsync(failedCallback);

                            await smtpClient.ExecMailFromAsync(mailFrom, failedCallback);

                            await smtpClient.ExecRcptToAsync(mailAddress, failedCallback);

                            await smtpClient.ExecDataAsync(msg.Message, failedCallback, async (response) => {
                                await msg.HandleDeliverySuccessAsync(sndIpAddress, smtpClient.MXRecord, response);
                            });

                            SmtpClientPool.Instance.Enqueue(smtpClient);

                            result = true;
                        }
                        catch (SmtpTransactionFailedException)
                        {
                            // Exception is thrown to exit transaction, logging of deferrals/failers already handled.
                            result = false;
                        }
                        catch (Exception ex)
                        {
                            Logging.Error("MessageSender error.", ex);
                            if (msg != null)
                            {
                                msg.HandleDeliveryDeferral("Connection was established but ended abruptly.", sndIpAddress, smtpClient.MXRecord, false);
                            }
                            result = false;
                        }
                        finally
                        {
                            if (smtpClient != null)
                            {
                                smtpClient.IsActive   = false;
                                smtpClient.LastActive = DateTime.UtcNow;
                            }
                        }
                    }
                }
            }
            return(result);
        }
Пример #11
0
        /// <summary>
        /// Queues the email for relaying.
        /// </summary>
        private async Task <SmtpServerTransactionAsyncResult> QueueForRelayingAsync()
        {
            // The email is for relaying.
            Guid messageID = Guid.NewGuid();

            // Look for any MTA control headers.
            MessageHeaderCollection headers = MessageManager.GetMessageHeaders(Data);

            // Will not be null if the SendGroupID header was present.
            MessageHeader ipGroupHeader = headers.SingleOrDefault(m => m.Name.Equals(MessageHeaderNames.SendGroupID, StringComparison.OrdinalIgnoreCase));

            // Parameter will hold the MtaIPGroup that will be used to relay this message.
            VirtualMtaGroup mtaGroup  = null;
            int             ipGroupID = 0;

            if (ipGroupHeader != null)
            {
                if (int.TryParse(ipGroupHeader.Value, out ipGroupID))
                {
                    mtaGroup = VirtualMtaManager.GetVirtualMtaGroup(ipGroupID);
                }
            }

            #region Look for a send id, if one doesn't exist create it.
            MessageHeader sendIdHeader   = headers.SingleOrDefault(h => h.Name.Equals(MessageHeaderNames.SendID, StringComparison.OrdinalIgnoreCase));
            int           internalSendId = -1;
            if (sendIdHeader != null)
            {
                Send sndID = await SendManager.Instance.GetSendAsync(sendIdHeader.Value);

                if (sndID.SendStatus == SendStatus.Discard)
                {
                    return(SmtpServerTransactionAsyncResult.FailedSendDiscarding);
                }
                internalSendId = sndID.InternalID;
            }
            else
            {
                Send sndID = await SendManager.Instance.GetDefaultInternalSendIdAsync();

                if (sndID.SendStatus == SendStatus.Discard)
                {
                    return(SmtpServerTransactionAsyncResult.FailedSendDiscarding);
                }
                internalSendId = sndID.InternalID;
            }
            #endregion

            #region Generate Return Path
            string returnPath = string.Empty;

            // Can only return path to messages with one rcpt to
            if (RcptTo.Count == 1)
            {
                // Need to check to see if the message contains a return path overide domain.
                MessageHeader returnPathDomainOverrideHeader = headers.SingleOrDefault(h => h.Name.Equals(MessageHeaderNames.ReturnPathDomain, StringComparison.OrdinalIgnoreCase));

                if (returnPathDomainOverrideHeader != null &&
                    MtaParameters.LocalDomains.Count(d => d.Hostname.Equals(returnPathDomainOverrideHeader.Value, StringComparison.OrdinalIgnoreCase)) > 0)
                {
                    // The message contained a local domain in the returnpathdomain
                    // header so use it instead of the default.
                    returnPath = ReturnPathManager.GenerateReturnPath(RcptTo[0], internalSendId, returnPathDomainOverrideHeader.Value);
                }
                else
                {
                    // The message didn't specify a return path overide or it didn't
                    // contain a localdomain so use the default.
                    returnPath = ReturnPathManager.GenerateReturnPath(RcptTo[0], internalSendId);
                }

                // Insert the return path header.
                Data = MessageManager.AddHeader(Data, new MessageHeader("Return-Path", "<" + returnPath + ">"));
            }
            else
            {
                // multiple rcpt's so can't have unique return paths, use generic mail from.
                returnPath = MailFrom;
            }
            #endregion

            #region Generate a message ID header
            string msgIDHeaderVal = "<" + messageID.ToString("N") + MailFrom.Substring(MailFrom.LastIndexOf("@")) + ">";

            // If there is already a message header, remove it and add our own. required for feedback loop processing.
            if (headers.Count(h => h.Name.Equals("Message-ID", StringComparison.OrdinalIgnoreCase)) > 0)
            {
                Data = MessageManager.RemoveHeader(Data, "Message-ID");
            }

            // Add the new message-id header.
            Data = MessageManager.AddHeader(Data, new MessageHeader("Message-ID", msgIDHeaderVal));
            #endregion

            #region Get message priority
            var msgPriority    = RabbitMqPriority.Low;
            var priorityHeader = headers.GetFirstOrDefault(MessageHeaderNames.Priority);
            if (priorityHeader != null)
            {
                var outVal = 0;
                if (int.TryParse(priorityHeader.Value, out outVal))
                {
                    if (outVal >= 0)
                    {
                        msgPriority = outVal < 3 ? (RabbitMqPriority)(byte)outVal
                                                 : RabbitMqPriority.High;
                    }
                }
            }
            #endregion

            // Remove any control headers.
            headers = new MessageHeaderCollection(headers.Where(h => h.Name.StartsWith(MessageHeaderNames.HeaderNamePrefix, StringComparison.OrdinalIgnoreCase)));
            foreach (MessageHeader header in headers)
            {
                Data = MessageManager.RemoveHeader(Data, header.Name);
            }

            // If the MTA group doesn't exist or it's not got any IPs, use the default.
            if (mtaGroup == null ||
                mtaGroup.VirtualMtaCollection.Count == 0)
            {
                ipGroupID = VirtualMtaManager.GetDefaultVirtualMtaGroup().ID;
            }

            // Attempt to Enqueue the Email for Relaying.
            var enqueued = await QueueManager.Instance.Enqueue(messageID, ipGroupID, internalSendId, returnPath, RcptTo.ToArray(), Data, msgPriority);

            return(enqueued ? SmtpServerTransactionAsyncResult.SuccessMessageQueued
                            : SmtpServerTransactionAsyncResult.FailedToEnqueue);
        }