/// <summary> /// Attempt to connect to the specified MX server. /// </summary> /// <param name="mx">MX Record of the server to connect to.</param> public async Task <bool> ConnectAsync(MXRecord mx) { _LastActive = DateTime.UtcNow; IsActive = true; await base.ConnectAsync(mx.Host, MtaParameters.Client.SMTP_PORT); _LastActive = DateTime.UtcNow; SmtpStream = new SmtpStreamHandler(this as TcpClient); _MXRecord = mx; // Read the Server greeting. string response = SmtpStream.ReadAllLines(); _LastActive = DateTime.UtcNow; if (!response.StartsWith("2")) { // 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 (response.StartsWith("421")) { ServiceNotAvailableManager.Add(SmtpStream.LocalAddress.ToString(), MXRecord.Host, DateTime.UtcNow); } base.Close(); return(false); } IsActive = false; _LastActive = DateTime.UtcNow; return(true); }
/// <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)); }
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; } } }