/// <summary> /// Handles the "RCPT TO:" SMTP command /// </summary> /// <param name="source">The event source.</param> /// <param name="eodArgs">The event arguments.</param> public void RcptToHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs) { //check if recipient exists in exchange address book if (this.addressBook != null && this.addressBook.Find(rcptArgs.RecipientAddress) != null) { //recipient is an existing user return; } string replaceWith = null; foreach (DomainElement d in domainList) { if (!d.Regex && d.Name.ToLower().Equals(rcptArgs.RecipientAddress.DomainPart.ToLower())) { replaceWith = d.Address; break; } else if (d.Regex) { if (d.RegexCompiled.Match(rcptArgs.RecipientAddress.ToString().ToLower()).Success) { replaceWith = d.RegexCompiled.Replace(rcptArgs.RecipientAddress.ToString().ToLower(), d.Address.ToLower()); break; } } } if (replaceWith != null) { RoutingAddress catchAllAddress = new RoutingAddress(replaceWith); if (this.databaseConnector == null || !this.databaseConnector.isBlocked(rcptArgs.RecipientAddress.ToString().ToLower())) { Logger.LogInformation("Caught: " + rcptArgs.RecipientAddress.ToString().ToLower() + " -> " + catchAllAddress.ToString(), 100); // on Exchange 2013 SP1 it seems the RcptToHandler is called multiple times for the same MailItem // hash code is not guaranteed to be unique. Add time to fix uniqueness string itemId = rcptArgs.MailItem.GetHashCode().ToString() + rcptArgs.MailItem.FromAddress.ToString(); if (!origToMapping.ContainsKey(itemId)) { string[] addrs = new string[] { rcptArgs.RecipientAddress.ToString().ToLower(), catchAllAddress.ToString().ToLower() }; origToMapping.Add(itemId, addrs); } rcptArgs.RecipientAddress = catchAllAddress; } else { // reject email, because address is blocked Logger.LogInformation("Recipient blocked: " + rcptArgs.RecipientAddress.ToString().ToLower(), 200); if (CatchAllFactory.AppSettings.RejectIfBlocked) { source.RejectCommand(CatchAllAgent.rejectResponse); } } } return; }
private void XSessionParamsCommandHandler(ReceiveCommandEventSource source, XSessionParamsCommandEventArgs args) { DeliveryThrottlingAgent.Diag.TraceDebug(0, (long)this.GetHashCode(), "XSessionParamsCommandHandler started"); if (DeliveryConfiguration.Instance.Throttling.DynamicMailboxDatabaseThrottlingEnabled) { DeliveryThrottling.Instance.ResetSession(args.SmtpSession.SessionId); this.destinationMdbGuid = args.DestinationMdbGuid; this.connectionManager = DeliveryThrottling.Instance.MailboxDatabaseCollectionManager.GetConnectionManager(args.DestinationMdbGuid); if (!DeliveryThrottling.Instance.CheckAndTrackDynamicThrottleMDBPendingConnections(this.destinationMdbGuid, this.connectionManager, args.SmtpSession.SessionId, args.SmtpSession.RemoteEndPoint.Address, out this.mdbHealthMonitors)) { MSExchangeStoreDriver.DeliveryRetry.Increment(); source.RejectCommand(AckReason.DynamicMailboxDatabaseThrottlingLimitExceeded); return; } } else if (!DeliveryThrottling.Instance.CheckAndTrackThrottleMDB(args.DestinationMdbGuid, args.SmtpSession.SessionId, out this.mdbHealthMonitors)) { MSExchangeStoreDriver.DeliveryRetry.Increment(); source.RejectCommand(AckReason.MailboxDatabaseThreadLimitExceeded); } }
private void RcptCommandHandler(ReceiveCommandEventSource source, RcptCommandEventArgs args) { DeliveryThrottlingAgent.Diag.TraceDebug(0, (long)this.GetHashCode(), "RcptCommandHandler started"); if (!DeliveryThrottling.Instance.CheckAndTrackThrottleRecipient(args.RecipientAddress, args.SmtpSession.SessionId, this.destinationMdbGuid.ToString(), args.MailItem.TenantId)) { MSExchangeStoreDriver.DeliveryRetry.Increment(); source.RejectCommand(AckReason.RecipientThreadLimitExceeded); } if (this.connectionManager != null) { this.connectionManager.UpdateLastActivityTime(args.SmtpSession.SessionId); } }
/// <summary> /// Invoked by Exchange when an SMTP RCPT command is sent. /// This is the command that gives you the recipient /// address in the SMTP envelope. /// </summary> /// <param name="source">The source of this event.</param> /// <param name="rcptArgs">Arguments for this event.</param> public void OnRcptCommandHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs) { // Check the parameter values. if (source == null || rcptArgs == null) { return; } // Skip filtering for internal mail. if (!rcptArgs.SmtpSession.IsExternalConnection) { return; } // Retrieve data used to identify this message. this.senderAddress = rcptArgs.MailItem.FromAddress; this.senderIP = rcptArgs.SmtpSession.RemoteEndPoint.Address; this.recipientAddress = rcptArgs.RecipientAddress; // If the sender domain is null, you will have to wait until // after the EndOfData event to check the message. if (RoutingAddress.NullReversePath.Equals(this.senderAddress)) { this.testOnEndOfHeaders = true; return; } // Skip temporary blocking for safe senders. if (this.ShouldBypassFilter(this.senderAddress, this.recipientAddress, this.server)) { return; } // Check the database to determine whether the message should be rejected // or let through. if (!this.VerifyTriplet(this.senderIP, this.senderAddress, this.recipientAddress)) { source.RejectCommand(DelayResponseMessage); } // Finally, check a few rows // for expired entries that need to be cleaned up. this.verifiedDatabase.RetireOldEntries( this.settings.CleanRowCount, this.settings.VerifiedExpirationTime); this.unverifiedDatabase.RetireOldEntries( this.settings.CleanRowCount, this.settings.UnverifiedExpirationTime); }
/// <summary> /// Handles the "RCPT TO:" SMTP command /// </summary> /// <param name="source">The event source.</param> /// <param name="rcptArgs"></param> public void RcptToHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs) { // Get the recipient address as a lowercase string. string strRecipientAddress = rcptArgs.RecipientAddress.ToString().ToLower(); // Search the banned email List for the recipient. bool exists = _catchAllConfig.Banned.Exists(element => element == strRecipientAddress); if (exists) { // If found respond to the sending MTA with the reject response. source.RejectCommand(RejectResponse); // No further processing. return; } // For each pair of regexps to email addresses foreach (var pair in _catchAllConfig.AddressMap) { // Create the regular expression and the routing address from the dictionary. var emailPattern = new Regex(pair.Key); RoutingAddress emailAddress = pair.Value; // If the recipient address matches the regular expression. if (emailPattern.IsMatch(strRecipientAddress)) { // And if the recipient is NOT in the address book. if ((_addressBook != null) && (_addressBook.Find(rcptArgs.RecipientAddress) == null)) { // Redirect the recipient to the other address. rcptArgs.RecipientAddress = emailAddress; // No further processing. return; } } } // No further processing. return; }
private void MailCommandHandler(ReceiveCommandEventSource source, MailCommandEventArgs args) { DeliveryThrottling.Instance.SetSessionMessageSize(args.Size, args.SmtpSession.SessionId); if (!DeliveryConfiguration.Instance.Throttling.DynamicMailboxDatabaseThrottlingEnabled) { return; } if (this.destinationMdbGuid == Guid.Empty || this.connectionManager == null) { DeliveryThrottlingAgent.Diag.TraceDebug <long>(0, (long)this.GetHashCode(), "Dynamic Throttling: No destination mailbox database specified for this connection, no throttling applied. SessionId {0}", args.SmtpSession.SessionId); return; } if (this.mailboxDatabaseConnectionInfo != null) { DeliveryThrottlingAgent.Diag.TraceDebug <long>(0, (long)this.GetHashCode(), "Dynamic Throttling: Connection was previously acquired. Being released before attempt to reacquire. SessionId {0}", args.SmtpSession.SessionId); this.connectionManager.Release(ref this.mailboxDatabaseConnectionInfo); } if (!DeliveryThrottling.Instance.CheckAndTrackDynamicThrottleMDBTimeout(this.destinationMdbGuid, this.mailboxDatabaseConnectionInfo, this.connectionManager, args.SmtpSession.SessionId, args.SmtpSession.RemoteEndPoint.Address, this.defaultConnectionWait, this.mdbHealthMonitors)) { DeliveryThrottling.Instance.DecrementCurrentMessageSize(args.SmtpSession.SessionId); source.RejectCommand(AckReason.DynamicMailboxDatabaseThrottlingLimitExceeded); } this.connectionManager.UpdateLastActivityTime(args.SmtpSession.SessionId); }
/// <summary> /// Handles the "RCPT TO:" SMTP command /// </summary> /// <param name="source">The event source.</param> /// <param name="rcptArgs"></param> public void RcptToHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs) { // Get the recipient address as a lowercase string. string strRecipientAddress = rcptArgs.RecipientAddress.ToString().ToLower(); // Search the banned email List for the recipient. bool exists = _catchAllConfig.Banned.Exists(element => element == strRecipientAddress); if (exists) { // If found respond to the sending MTA with the reject response. source.RejectCommand(RejectResponse); // No further processing. return; } // For each pair of regexps to email addresses foreach (var pair in _catchAllConfig.AddressMap) { // Create the regular expression and the routing address from the dictionary. var emailPattern = new Regex(pair.Key); RoutingAddress emailAddress = pair.Value; // If the recipient address matches the regular expression. if (emailPattern.IsMatch(strRecipientAddress)) { // And if the recipient is NOT in the address book. if ((_addressBook != null) && (_addressBook.Find(rcptArgs.RecipientAddress) == null)) { // Redirect the recipient to the other address. rcptArgs.RecipientAddress = emailAddress; // No further processing. return; } } } // No further processing. return; }
public void OnRcptCommandHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs) { // Check the parameter values. if (source == null || rcptArgs == null) { this.logLine("ERROR: Source or Recipient Argements was null", 1); return; } // Skip filtering for internal mail. if (!rcptArgs.SmtpSession.IsExternalConnection) { if (rcptArgs.RecipientAddress.ToString().IndexOf("HealthMailbox") == 0) { return; } this.logLine("FROM=, TO=" + rcptArgs.RecipientAddress.ToString() + ", REMOTE=" + rcptArgs.SmtpSession.RemoteEndPoint.Address.ToString() + ", STATE=Bypassed, REASON=Internal Connection", 2); return; } // If the sender domain is null, you will have to wait until // after the EndOfData event to check the message. if (RoutingAddress.NullReversePath.Equals(rcptArgs.MailItem.FromAddress)) { this.testOnEndOfHeaders = true; return; } // Check to see if whitelisted or in safe senders list if (this.ShouldBypassFilter(rcptArgs.MailItem.FromAddress, rcptArgs.RecipientAddress, rcptArgs.SmtpSession.RemoteEndPoint.Address)) { this.logLine("FROM=" + rcptArgs.MailItem.FromAddress.ToString() + ", TO=" + rcptArgs.RecipientAddress.ToString() + ", REMOTE=" + rcptArgs.SmtpSession.RemoteEndPoint.Address.ToString() + ", STATE=Bypassed, REASON=Filter Bypass Match", 2); return; } // Check the database to determine whether the message should be rejected // or let through. if (!this.VerifyTriplet(rcptArgs.SmtpSession.RemoteEndPoint.Address, rcptArgs.MailItem.FromAddress, rcptArgs.RecipientAddress)) { this.logLine("FROM=" + rcptArgs.MailItem.FromAddress.ToString() + ", TO=" + rcptArgs.RecipientAddress.ToString() + ", REMOTE=" + rcptArgs.SmtpSession.RemoteEndPoint.Address.ToString() + ", STATE=Greylist, REASON=Triplet verification Fail", 2); source.RejectCommand(DelayResponseMessage); return; } this.logLine("FROM=" + rcptArgs.MailItem.FromAddress.ToString() + ", TO=" + rcptArgs.RecipientAddress.ToString() + ", REMOTE=" + rcptArgs.SmtpSession.RemoteEndPoint.Address.ToString() + ", STATE=Accept, REASON=Triplet Match.", 2); // Finally, check a few rows // for expired entries that need to be cleaned up. this.greylistDatabase.Clean(this.settings.CleanRowCount, this.settings.ConfirmedMaxAge, this.settings.UnconfirmedMaxAge); }