65.9K
CodeProject is changing. Read more.
Home

Exchange Domain Mail SMTP Sink

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (4 votes)

Nov 1, 2006

2 min read

viewsIcon

55185

downloadIcon

635

How to create a domain mail sink for Exchange Server.

Active Directory Users and Computers - notice @foodcandy.com

Introduction

A while ago, I started running Microsoft Small Business Server with Microsoft Exchange 2003. I use Outlook with RPC over HTTP, synchronize with Exchange on my Smart Phone, and use OWA. I also host a few domains on my server.

When I signup for a service, I want to give service@mydomain.com as my e-mail address, so that I can block spam. It’s easy to add SMTP addresses to Active Directory accounts, but I have accounts on a thousand services! Hence the following question: how do I get *@mydomain.com routed in Exchange?

If you just want to install this sink, download the binaries and run RegisterSink.cmd. Add @domain.com to users that should receive all e-mail @domain.com. Run UnregisterSink to uninstall.

Background

These two articles are an excellent start, but I have more than one domain and I want this to be configurable.

What does this code do?

  • Implement IMailTransportSubmission::OnMessageSubmission to catch submitted e-mail messages.
  • Search for an Active Directory user that has an exactly matching mail or proxy address.
  • Search for an Active Directory user that has a matching @domain.com mail or proxy address.
  • Route e-mail accordingly.
  • Log routing events to the event log.

Implementation Details

Please note that the samples below are stripped of error checking. Please see the actual source code attached.

First, I had to follow the details in Writing Managed Sinks for SMTP and Transport Events and create the managed proxy objects to receive SMTP events.

Code to connect to Active Directory code is widely available. The slightly tricky parts are connecting to the RootDSE and finding the Users node.

//
// Root DSE and the Users LDAP path
//
 
mRootDSE = new DirectoryEntry(
 "LDAP://" + (DomainName.Length > 0 ? DomainName + "/" : string.Empty) +
 "RootDSE");
 
mUsersLDAPPath = "LDAP://" + 
 (DomainName.Length > 0 ? DomainName + "/" : string.Empty) + 
 "CN=Users," + RootDSE.Properties["defaultNamingContext"].Value;

When an e-mail arrives, it will be potentially re-routed. A new list of recipients must be created.

void IMailTransportSubmission.OnMessageSubmission(
 MailMsg message,
 IMailTransportNotify notify,
 IntPtr context)
{
 Message Msg = new Message(message);
 RecipsAdd NewRecipients = Msg.AllocNewList();
 bool fReRouted = false;
 
 foreach (Recip Recipient in Msg.Recips)
 {
     fReRouted |= ReRoute(Recipient, Msg, NewRecipients);
 }
 
 if (fReRouted)
 {
     Msg.WriteList(NewRecipients);
 }
 
 Marshal.ReleaseComObject(message);
}

And the meat of the re-routing:

private bool ReRoute(Recip Recipient, Message Msg, 
                     RecipsAdd NewRecipients)
{
     ActiveDirectory Directory = new ActiveDirectory();
 
     string[] SearcherPropertiesToLoad = { "cn", "mail", 
                                     "proxyAddresses" };
 
     DirectorySearcher Searcher = new DirectorySearcher(
      new DirectoryEntry(Directory.UsersLDAPPath),
      "(&(objectCategory=person)(objectClass=user)(| 
      (proxyAddresses=*smtp:@" + 
      Recipient.SMTPAddressDomain.ToLower() + 
      "*)(proxyAddresses=*smtp:" + 
      Recipient.SMTPAddress + "*)))",
      SearcherPropertiesToLoad);
 
     SearchResultCollection SearchResults = Searcher.FindAll();
 
     if (SearchResults.Count == 0) return false;
 
     foreach (SearchResult SearchResult in SearchResults)
     {
      foreach (string ProxyAddressProperty in 
               SearchResult.Properties["proxyAddresses"]) 
      {
       string ProxyAddress = ProxyAddressProperty.ToLower();
       if ("smtp:" + Recipient.SMTPAddress.ToLower() == 
                                           ProxyAddress)
       {
        // there's an address that matches
        // exactly, add him to the re-routing
        // list because there might be other
        // recipients that don't match and
        // will require routing
        NewRecipients.AddSMTPRecipient(
               Recipient.SMTPAddress, null);
        return false;
       }
      }
     }
 
     foreach (SearchResult SearchResult in SearchResults)
     {
      foreach (string ProxyAddressProperty in 
               SearchResult.Properties["proxyAddresses"])
      {
       string ProxyAddress = ProxyAddressProperty.ToLower();
 
       // this is necessary to avoid matching
       // @foo.com with @foo.com.bar
       if ("smtp:@" + Recipient.SMTPAddressDomain.ToLower() == 
                                                  ProxyAddress)
       {
        string RoutedSMTPAddress = 
               SearchResult.Properties["mail"][0].ToString();
 
        NewRecipients.AddSMTPRecipient(RoutedSMTPAddress, null);
        return true;
       }
      }
     }
 
     return false;
 }

That's it. This was overall a simple task, and should, frankly, ship with Microsoft Exchange. This code can probably be easily extended to support regular expressions and other fancy things.

History

  • 10/24/2006: initial release.