Пример #1
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));
        }
Пример #2
0
        public async Task <MantaOutboundClientSendResult> SendAsync(MailAddress mailFrom, MailAddress rcptTo, string msg, bool isRetry = false)
        {
            try
            {
                MantaOutboundClientSendResult result = null;
                if (!TcpClient.Connected)
                {
                    if (ServiceNotAvailableManager.IsServiceUnavailable(_VirtualMta.IPAddress.ToString(), MXRecord.Host.ToLower()))
                    {
                        return(new MantaOutboundClientSendResult(MantaOutboundClientResult.ServiceNotAvalible, null, _VirtualMta, _MXRecord));
                    }

                    result = await ConnectAsync();

                    if (result.MantaOutboundClientResult != MantaOutboundClientResult.Success)
                    {
                        if (result.MantaOutboundClientResult == MantaOutboundClientResult.ServiceNotAvalible ||
                            result.MantaOutboundClientResult == MantaOutboundClientResult.FailedToConnect)
                        {
                            ServiceNotAvailableManager.Add(_VirtualMta.IPAddress.ToString(), MXRecord.Host.ToLower(), DateTime.UtcNow);
                        }

                        return(result);
                    }
                }
                else
                {
                    result = await ExecRsetAsync();

                    if (result.MantaOutboundClientResult != MantaOutboundClientResult.Success)
                    {
                        return(result);
                    }
                }

                // MAIL FROM
                result = await ExecMailFromAsync(mailFrom);

                if (result.MantaOutboundClientResult != MantaOutboundClientResult.Success)
                {
                    return(result);
                }

                // RCPT TO
                result = await ExecRcptToAsync(rcptTo);

                if (result.MantaOutboundClientResult != MantaOutboundClientResult.Success)
                {
                    return(result);
                }

                // DATA
                result = await ExecDataAsync(msg);

                if (result.MantaOutboundClientResult == MantaOutboundClientResult.Success &&
                    _MaxMessagesConnection.HasValue &&
                    _MessagesAccepted >= _MaxMessagesConnection)
                {
                    // MAX MESSAGES: QUIT
                    await ExecQuitAsync();

                    _MessagesAccepted = 0;
                }

                return(result);
            }catch (Exception ex)
            {
                if (ex is ObjectDisposedException)
                {
                    if (isRetry)
                    {
                        return(new MantaOutboundClientSendResult(MantaOutboundClientResult.FailedToConnect, "500 Failed to connect", _VirtualMta, _MXRecord));
                    }
                    else
                    {
                        return(await SendAsync(mailFrom, rcptTo, msg, true));
                    }
                }

                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, "400 Try again later", _VirtualMta, _MXRecord));
            }
            finally
            {
                lock (_inUseLock)
                {
                    InUse = false;
                }
            }
        }