in Development, Internet, Microsoft, Software

RegExCatchAll Transport Agent for Exchange 2007/2010

For a number of years now I have been doing two things with email:

1) I run my own Exchange ServerExchange 2010 LogoIt provides ActiveSync, Outlook Web Access, and Outlook Anywhere for my company’s email domains and my personal durdle.com domain.

2) I provide email addresses at durdle.com for family members (and a few people who just happen to share the family name).

Historically all those family emails have been maintained by Gradwell without going near my Exchange Server.  The durdle.com MX records in DNS pointed to Gradwell’s servers for mail delivery and I had a forwarder at Gradwell that would redirect my – and only my – messages to the Exchange Server.  For a number of reasons I needed to change this so that all email is delivered via my Exchange Server.  I made this change yesterday, discovering one problem in the process.

Email Suffixes

Gradwell’s mail server supports email suffix matching.  If you have a GMail account you may be familiar with the approach.  Say you have an email john@gmail.com.  Gmail will actually deliver mail to john+anything@gmail.com; you can add any suffix you like after the + and GMail will still deliver it to your inbox.  Gradwell support this by allowing you to place a period after the local part so you can use john.anything@example.com.  You don’t have to create these email addresses anywhere – you can just start using it and the mail server will make sure it gets to you.

This is fantastically useful because it means that when a website asks for your email address you can give out a custom throwaway email just for that site.  So when you sign up at DodgyCo.com, you give them the email john.dodgyco@example.com.  If you ever receive spam because DodgyCo sold your email address you’ll know a) who sold you out, and b) which email address to add to your spam filter.

Exchange Server

Enter Microsoft Exchange Server.  I’m currently running the 2010 edition, but the problem I’m about to describe (and solve!) exists in every recent version.  Exchange Server doesn’t support email suffixes.  Every recipient has to have an alias created on the server.  If you want to deliver to john.suffix@example.com you’d better remember to create an alias for that user, otherwise the Exchange Recipient Filter will reject the message since – as far as it is concerned – the user does not exist on the server.

Worse, Exchange doesn’t even implement any generic CatchAll functionality.  CatchAll would allow the server to redirect messages for non-existing recipients to a specific address. Smart people have created solutions to the CatchAll problem but all that does is allow you to define one mailbox for ALL non-deliverable messages to be delivered to.  Since I need to provide suffix matching to multiple users this doesn’t work for me.

Time to extend Exchange’s functionality!

Transport Agents

Transport Agents were introduced in Exchange 2007 to replace Exchange 2003’s SMTP Event Sinks and have been retained largely unchanged in Exchange 2010.  Broadly speaking when a message arrives at an Exchange Server it is moved through the transport pipeline as each SMTP event occurs.  Each SMTP event (Connect, MAIL FROM, RCPT TO, etc) can trigger an interested Transport Agent to perform some actions on the message.
The complete list of SMTP events and their sequence is described on this Exchange Server TechCenter page.

Transport Agents are simply DLLs written in managed code that fire on SMTP events.  Back when I contracted for the MOD I wrote a Transport Agent for Exchange 2007 which demonstrated that Exchange was capable of performing Dominance Checking on outgoing emails so I knew that it wouldn’t take to long to write a Transport Agent to filter incoming messages.  I was able to reuse some of that code and also made use of some of Wilbert De Graaf’s CatchAll Agent code which made the handling of a configuration file very quick to implement.  Thank you Wilbert!

Requirements

I had a couple of requirements for my Transport Agent:

1) It should allow redirection of multiple suffixes to different recipients.

2) It should allow me to ban delivery to any previously used suffixes and ensure that the sender receives a non-deliverable report.

I ended up meeting these requirements by using C# to write a Transport Agent that accepts its configuration from an XML file.  The config.xml defines multiple regular expressions to match against along with associated redirect addresses.  It also lists banned email addresses which the Transport Agent should reject.

To mimic the Gradwell behaviour which we were already making use of I needed to match firstname then a dot and then any word before the domain.  This is a fairly simple regular expression in C#, and looks like this:

^firstname+\.[a-z]+@example.com$

The config.xml takes the form:

<config>
    <redirect pattern="^john+\.[a-z]+@domain.com$" address="john@domain.com" />
    <redirect pattern="^jane+\.[a-z]+@domain.com$" address="jane.doe@gmail.com" />
    <banned address="john.spam@domain.com" />
    <banned address="jane.newsletter@domain.com" />
</config>

Here we define two patterns for redirect, one to an internal recipient and one redirect to an external SMTP address.  We also ban two known “bad” recipient addresses.

You’ll need to provide at least one redirect entry, but you’re not required to ban any addresses if you don’t want to.  Also, while redirects specifiy regular expressions to match, banned addresses are just strings which are simply compared. I leave it as an exercise for the reader to add regular expressions to the banned list!

The Code

Whenever the config.xml file is changed, the agent reloads it and parses the XML.  It puts the banned emails into a List and the regular expression patterns and redirect addresses into a Dictionary.  If any of the emails in the config appear invalid the new configuration is rejected and the agent continues to use the running config.

The agent defines a RCPT TO: handler.  This event is fired as soon as the sending MTA starts to transmit the recipient addresses. rcptArgs contains the argument we need: RecipientAddress, which we store as a lowercase string for comparison. Although the spec (RFC2821) actually requires email addresses to be case sensitive, I’ve never met an MTA that adheres to that part of the spec. So for our purposes email addresses are case insensitive and we can do all comparisons in lowercase.

public void RcptToHandler(ReceiveCommandEventSource source, RcptCommandEventArgs rcptArgs)
{
// Get the recipient address as a lowercase string.
string strRecipientAddress = rcptArgs.RecipientAddress.ToString().ToLower();

It performs two checks. First – is the recipient address in the ban list?

// 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(CatchAllAgent.rejectResponse);
 
	// No further processing.
	return;
}

And second, does the recipient address match any of the regular expressions?

// 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.
	Regex 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 ((this.addressBook != null) &&
			(this.addressBook.Find(rcptArgs.RecipientAddress) == null))
		{
			// Redirect the recipient to the other address.
			rcptArgs.RecipientAddress = emailAddress;
 
			// No further processing.
			return;
		}
	}
}

Note that in both cases as soon as we’ve got a positive result we stop further processing. If an address is in the ban list we immediately reject it, and if we find a matching regular expression we use its paired address to redirect the message and stop looking for further matches.

I’ve given enough information here that a C# developer with some spare time could create this Transport Agent themselves. However I know that it is really useful to have a package you can just download and install without having to worry about compiling it yourself. So I’ve packaged the DLL together with installation scripts and the install and configuration guide.

Please visit the product page for more information and to purchase the Transport Agent. Payment is via PayPal, you will receive the download link within minutes of payment.

  1. This post was awesome! I’m currently figuring out how to migrate an Exchange 2003 COM+ Store Event Sink to Exchange 2010, so this was very helpful to me.

    In my current Store Event Sink y have used the Asynchronous Managed Events, which I then registered for one particular Store (at the end is registered for one email address). So my code only runs when the OnSave method to that particular Store is raised (when an email arrives at that particular email address). What you have described here is a Transport Agent which runs on ALL emails that arrive at the Exchange Server, regardless of which email address it is used for the Recipient. So, my question is as follows: Is there anything similar to the Asynchronous Store Events Sinks in Exchange 2003 in Exchange 2010 that you know of?

    I’m looking into this as I don’t want to have to check every single email because I need only to check the emails specific to one address as they arrive.

    Thanks in advanced!

  2. What .net version is your dll compiled to?
    did you encounter issues with .net 4 with Ex2010?

    it seems that it doesnt support .net 4 dlls.

Comments are closed.