/// <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 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); }
/// <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> /// 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); }