/// <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 #2
0
        public void TestDefaultRules()
        {
            int mxPatternID = 0;
            OutboundRuleCollection rules = OutboundRuleManager.GetRules(new MantaMTA.Core.DNS.MXRecord("localhost", 10, 10, DNS.MxRecordSrc.A), new MantaMTA.Core.VirtualMta.VirtualMTA()
            {
                ID = 0, IPAddress = System.Net.IPAddress.Parse("127.0.0.1")
            }, out mxPatternID);

            Assert.AreEqual(3, rules.Count);
        }
Exemple #3
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 #4
0
        /// <summary>
        /// Attempt to create a new connection using the specified ip address and mx record.
        /// </summary>
        /// <returns>A connected outbound client or NULL</returns>
        public async Task <CreateNewConnectionAsyncResult> CreateNewConnectionAsync(VirtualMTA ipAddress, MXRecord mxRecord)
        {
            SmtpOutboundClient smtpClient = null;

            // Get the maximum connections to the destination.
            int maximumConnections = OutboundRuleManager.GetMaxConnectionsToDestination(ipAddress, mxRecord);

            lock (this.SyncRoot)
            {
                // Get the currently active connections count.
                int currentConnections = InUseConnections.Count;

                lock (_ConnectionAttemptsInProgressLock)
                {
                    // If the current connections count + current connection is less than
                    // the maximum connections then we can create a new connection otherwise
                    // we are maxed out so return null.
                    if (maximumConnections <= (currentConnections + _ConnectionAttemptsInProgress))
                    {
                        return(new CreateNewConnectionAsyncResult(new MaxConnectionsException()));
                    }


                    // Limit the amount of connection attempts or experiance massive delays 30s+ for client.connect()
                    if (_ConnectionAttemptsInProgress >= SmtpClientQueue.MAX_SIMULTANEOUS_CLIENT_CONNECT_ATTEMPTS)
                    {
                        //Logging.Debug("Cannot attempt to create new connection.");
                        return(new CreateNewConnectionAsyncResult(new MaxConnectionsException()));
                    }

                    //Logging.Debug("Attempting to create new connection.");
                    _ConnectionAttemptsInProgress++;
                }
            }

            // Do the actual creating and connecting of the client outside of the lock
            // so we don't block other threads.

            try
            {
                // Create the new client and make the connection
                smtpClient = new SmtpOutboundClient(ipAddress);
                await smtpClient.ConnectAsync(mxRecord);

                smtpClient.IsActive = true;
                this.InUseConnections.Add(smtpClient);
            }
            catch (Exception ex)
            {
                // If something went wrong clear the client so we don't return something odd.
                if (smtpClient != null)
                {
                    smtpClient.Close();
                    smtpClient.Dispose();
                    smtpClient = null;
                }
                if (ex is SocketException)
                {
                    return(new CreateNewConnectionAsyncResult(ex));
                }

                if (ex is AggregateException && ex.InnerException is System.IO.IOException)
                {
                    return(new CreateNewConnectionAsyncResult(ex.InnerException));
                }
            }
            finally
            {
                // Reduce the current attempts as were done.
                _ConnectionAttemptsInProgress--;
                if (smtpClient != null)
                {
                    smtpClient.IsActive = false;
                }
            }

            // Return connected client or null.
            return(new CreateNewConnectionAsyncResult(smtpClient));
        }
        /// <summary>
        /// Send the data to the server
        /// </summary>
        /// <param name="data">Data to send to the server</param>
        /// <param name="failedCallback">Action to call if fails to send.</param>
        public async Task <bool> ExecDataAsync(string data, Action <string> failedCallback, Func <string, Task> successCallbackAsync)
        {
            if (!base.Connected)
            {
                return(false);
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = true;

            await SmtpStream.WriteLineAsync("DATA");

            string response = await SmtpStream.ReadAllLinesAsync();

            // If the remote MX supports pipelining then we need to check the MAIL FROM and RCPT to responses.
            if (_CanPipeline)
            {
                // Check MAIL FROM OK.
                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                    IsActive = false;
                    return(false);
                }


                // Check RCPT TO OK.
                response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                    IsActive = false;
                    return(false);
                }

                // Get the Data Command response.
                response = await SmtpStream.ReadAllLinesAsync();
            }

            _LastActive = DateTime.UtcNow;

            if (!response.StartsWith("354"))
            {
                failedCallback(response);
                IsActive = false;
                return(false);
            }

            // Increment the data commands as server has responded positiely.
            _DataCommands++;

            // Send the message data using the correct transport MIME
            SmtpStream.SetSmtpTransportMIME(_DataTransportMime);
            await SmtpStream.WriteAsync(data, false);

            await SmtpStream.WriteAsync(MtaParameters.NewLine + "." + MtaParameters.NewLine, false);

            _LastActive = DateTime.UtcNow;

            // Data done so return to 7-Bit mode.
            SmtpStream.SetSmtpTransportMIME(SmtpTransportMIME._7BitASCII);


            response = await SmtpStream.ReadAllLinesAsync();

            _LastActive = DateTime.UtcNow;
            IsActive    = false;


            if (!response.StartsWith("250"))
            {
                failedCallback(response);
            }
            else
            {
                await successCallbackAsync(response);
            }


            // If max messages have been sent quit the connection.
            if (_DataCommands >= OutboundRuleManager.GetMaxMessagesPerConnection(MXRecord, MtaIpAddress))
            {
                ExecQuitAsync().Wait();
            }

            return(true);
        }