Example #1
0
        /// <summary>
        /// Enqueue the SmtpOutboundClient for use by another message.
        /// </summary>
        /// <param name="client">The client to queue.</param>
        public void Enqueue(SmtpOutboundClient client)
        {
            SmtpClientMxRecords mxConnections = this._OutboundConnections.GetOrAdd(client.SmtpStream.LocalAddress.ToString(), new SmtpClientMxRecords());
            SmtpClientQueue     clientQueue   = null;

            lock (this._ClientPoolLock)
            {
                if (!mxConnections.TryGetValue(client.MXRecord.Host, out clientQueue))
                {
                    clientQueue = new SmtpClientQueue();
                    if (!mxConnections.TryAdd(client.MXRecord.Host, clientQueue))
                    {
                        throw new Exception("Failed to add new SmtpClientQueue");
                    }
                }
            }
            clientQueue.Enqueue(client);
            try
            {
                clientQueue.InUseConnections.Remove(client);
            }
            catch (Exception)
            {
                // Already removed.
            }
        }
Example #2
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));
        }
Example #3
0
 private SmtpClientPool()
 {
     new Thread(new ThreadStart(delegate
     {
         while (true)
         {
             try
             {
                 IEnumerator <KeyValuePair <string, SmtpClientMxRecords> > enumMXs = this._OutboundConnections.GetEnumerator();
                 while (enumMXs.MoveNext())
                 {
                     int count   = 0;
                     int removed = 0;
                     KeyValuePair <string, SmtpClientMxRecords> current = enumMXs.Current;
                     SmtpClientMxRecords mxRecords = current.Value;
                     IEnumerator <KeyValuePair <string, SmtpClientQueue> > enumClients = mxRecords.GetEnumerator();
                     while (enumClients.MoveNext())
                     {
                         count++;
                         KeyValuePair <string, SmtpClientQueue> current2 = enumClients.Current;
                         SmtpClientQueue clients = current2.Value;
                         if (clients.Count == 0 && clients.InUseConnections.Count == 0)
                         {
                             ConcurrentDictionary <string, SmtpClientQueue> arg_8B_0 = mxRecords;
                             current2 = enumClients.Current;
                             if (arg_8B_0.TryRemove(current2.Key, out clients))
                             {
                                 //string arg_AF_0 = "Removed empty SMTP Clients Queue for ";
                                 //current2 = enumClients.Current;
                                 //Logging.Debug(arg_AF_0 + current2.Key);
                                 removed++;
                             }
                             else
                             {
                                 string arg_D6_0 = "Failed to remove empty SMTP Clients Queue for ";
                                 current2        = enumClients.Current;
                                 Logging.Debug(arg_D6_0 + current2.Key);
                             }
                         }
                     }
                     Logging.Debug(string.Concat(new string[]
                     {
                         "SmtpClientPool : Removed ",
                         removed.ToString("N0"),
                         " client queues. ",
                         (count - removed).ToString("N0"),
                         " remaining."
                     }));
                 }
             }
             catch (Exception ex)
             {
                 Logging.Debug("SmtpClientPool: " + ex);
             }
             finally
             {
                 Thread.Sleep(60000);
             }
         }
     }))
     {
         IsBackground = true
     }.Start();
 }