HOW TO:Rewrite the To address in Transport Agents on a Hub Server


Have you ever tried sending a mail to someone and it end up with someone else! Beware there could be a Transport Agent that's doing this:-).

I ran into an issue where I needed to rewrite the address the mail was being sent to. Not going into too much as to why somebody would want to do this,  I wrote a Transport Agent that would do the needful.

The below sample C# code written in Visual Studio 2005 changes the To address of the mail that is being sent out. There is no conditional logic as of now and the code assumes that there is only one recipient.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Microsoft.Exchange.Data.Mime;
using Microsoft.Exchange.Data.Transport;
using Microsoft.Exchange.Data.Transport.Email;
using Microsoft.Exchange.Data.Transport.Routing;

namespace Samples.Agents.MyRoutingAgent
{
    public class MyRoutingAgentFactory :RoutingAgentFactory
    {
        public override RoutingAgent CreateAgent(SmtpServer server)
        {
            return new MyRoutingAgent();
        }
    }

    public class MyRoutingAgent : RoutingAgent 
    {
        private object fileLock = new object();  

        public MyRoutingAgent()
        {
            base.OnSubmittedMessage += new SubmittedMessageEventHandler(MyRoutingAgent_OnSubmittedMessage);
        }

        void MyRoutingAgent_OnSubmittedMessage(SubmittedMessageEventSource source, QueuedMessageEventArgs e)
        {
            lock (fileLock)
            {
                try
                {
                    string logDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\Log";
                    string logFile = logDir + @"\log.txt";

                    if (!Directory.Exists(logDir))
                    {
                        Directory.CreateDirectory(logDir);
                    }
                    
                    if (!File.Exists(logFile))
                    {
                        File.CreateText(logFile).Close();
                    }

                    using (StreamWriter logWriter = File.AppendText(logFile))
                    {
                        logWriter.Write(Environment.NewLine + "-------------------------------------------------------------------------------" + Environment.NewLine);

                        //Alter the P1 headers.The P1 address is used for routing.
                        for(int intCounter=e.MailItem.Recipients.Count-1;intCounter>=0; intCounter--)
                        {
                            logWriter.WriteLine("Original Recipient:" + e.MailItem.Recipients[intCounter].OriginalRecipient);

                            if (e.MailItem.Recipients[intCounter].Address.IsValid)
                            {
                                e.MailItem.Recipients.Remove(e.MailItem.Recipients[intCounter]);

                                if (e.MailItem.Recipients.CanAdd)
                                {
                                    e.MailItem.Recipients.Add(new RoutingAddress("administrator@mycompany.com"));
                                }
                            }
                        }

                        //Alter the P2 headers so that the mail displays the correct recipient display Name
                        EmailRecipientCollection erToRecipientCollection;
                        erToRecipientCollection = e.MailItem.Message.To;

                        logWriter.Write(Environment.NewLine + "-------------------------------------------------------------------------------" + Environment.NewLine);
                        foreach (EmailRecipient rec in erToRecipientCollection)
                        {
                            logWriter.WriteLine("Original Display Name:" + rec.DisplayName);
                            logWriter.WriteLine("Original SMTP address:" + rec.SmtpAddress);

                            rec.DisplayName = "Administrator";
                            rec.SmtpAddress = "administrator@mycompany.com";

                            logWriter.WriteLine("Changed to <Administrator>administrator@mycompany.com");

                            }
                        }
                        logWriter.Write(Environment.NewLine + "-------------------------------------------------------------------------------" + Environment.NewLine);

                        logWriter.Flush();
                    }
                }
                catch (System.IO.IOException ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }
            return;
        }

    }
}

It is IMPORTANT that you change both the P1 and the P2 headers for this to work correctly. To build the Agent successfully you will need to add references to Microsoft.Exchange.Data.Transport and the Microsoft.Exchange.Data.Common namespaces. These dll's can be found in the Program Files\Microsoft\Exchange Server\Public folder.

To Install/Uninstall the Agent on the Hub Server I have created script files that do the job for me(makes life easier).

Install.PS1

#Copy the agent Dll to the C:\MyAgent folder.
$EXDIR="C:\MyAgent"
Net Stop MSExchangeTransport

Write-Output "Registering agent"
Install-TransportAgent -Name "My Routing Agent Sample" -AssemblyPath $EXDIR\RoutingAgent.dll -TransportAgentFactory Samples.Agents.MyRoutingAgent.MyRoutingAgentFactory

Write-Output "Enabling agent"
Enable-TransportAgent -Identity "My Routing Agent Sample"
Get-TransportAgent -Identity "My Routing Agent Sample"

Net Start MSExchangeTransport

Write-Output "Install Complete. Please exit the Exchange Management Shell."

UnInstall.PS1

$EXDIR="C:\MyAgent"
Net Stop MSExchangeTransport

Write-Output "Disabling Agent..."
Disable-TransportAgent -Identity "My Routing Agent Sample" -Confirm:$false

Write-Output "Uninstalling Agent.."
Uninstall-TransportAgent -Identity "My Routing Agent Sample" -Confirm:$false

Net Start MsExchangeTransport

Write-Output "Uninstall Complete."

Need more information on Transport Agents?

Transport Agents

http://msdn.microsoft.com/en-us/library/aa579185.aspx

Enjoy!

Comments (5)

  1. josh says:

    akash,

    i have a situation where we need to do a rewrite on all mail outbound for a specific domain and rewrite the headers to replace domain one with domain two. (essentially recreating the address rewrite outbound agent is the idea, to put on a hub/transport server).

    i see address rewrite is very easy if you have an Exchange 2007 edge server installed, but for a small org, having a second, dedicated edge server is not that feasible.

    i have been trying to hash through your code to rewrite it to do what i am looking for, but havent gotten it to work so far.

    here is a ruby script that gives the just of what i am trying to do to clarify the logic:

    email_header_rewrite_v1.rb

    original_email_address = "12345678900@company.fax"

    new_email_address = original_email_address.gsub(/company.fax$/, ‘othercompany.com’)

    puts new_email_address

    12345678900@othercompany.com

    any suggestions on what i need to do to get this to work? thanks for posting this by the way, good stuff 😉

  2. akash says:

    You can do this on the hub server too.

    All you have to do is:

    1)Save the LocalPart  before deleting the recepient

    strLocalPart =e.MailItem.Recipients[intCounter].Address.LocalPart

    2) Delete the recepient

    3) Add the new email as a new RoutingAddress.

    e.MailItem.Recipients.Add(new RoutingAddress(strLocalPart + "@othercompany.com"));

  3. josh says:

    akash, here is what i have come up with so far. any suggestions or improvements you think would be more effective?

    thanks!

    using System;

    using System.Collections;

    using System.Collections.Generic;

    using System.Diagnostics;

    using System.IO;

    using System.Threading;

    using System.Text.RegularExpressions;

    using Microsoft.Exchange.Data.Mime;

    using Microsoft.Exchange.Data.Transport;

    using Microsoft.Exchange.Data.Transport.Email;

    using Microsoft.Exchange.Data.Transport.Routing;

    namespace Samples.Agents.MyRoutingAgent

    {

       public class MyRoutingAgentFactory :RoutingAgentFactory

       {

           public override RoutingAgent CreateAgent(SmtpServer server)

           {

               return new MyRoutingAgent();

           }

       }

       public class MyRoutingAgent : RoutingAgent

       {

           private object fileLock = new object();  

           public MyRoutingAgent()

           {

               base.OnSubmittedMessage += new SubmittedMessageEventHandler(MyRoutingAgent_OnSubmittedMessage);

           }

           void MyRoutingAgent_OnSubmittedMessage(SubmittedMessageEventSource source, QueuedMessageEventArgs e)

           {

               lock (fileLock)

               {

                   try

                   {

                       string logDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"Log";

                       string logFile = logDir + @"log.txt";

                       if (!Directory.Exists(logDir))

                       {

                           Directory.CreateDirectory(logDir);

                       }

                       if (!File.Exists(logFile))

                       {

                           File.CreateText(logFile).Close();

                       }

                       using (StreamWriter logWriter = File.AppendText(logFile))

                       {

                           logWriter.Write(Environment.NewLine + "——————————————————————————-" + Environment.NewLine);

                           //Alter the P1 headers.The P1 address is used for routing.

                           for(int intCounter=e.MailItem.Recipients.Count-1;intCounter>=0; intCounter–)

                           {

                               logWriter.WriteLine("Original Recipient:" + e.MailItem.Recipients[intCounter].OriginalRecipient);

                               if (e.MailItem.Recipients[intCounter].Address.IsValid)

                               {

                                   //Save the whole email address and local part of the address to strings

                                   strEmailAddress = e.MailItem.Recipients[intCounter]

                                   strLocalPart = e.MailItem.Recipients[intCounter].Address.LocalPart

                                   //Create a regex that finds any emails going to d+@company.fax

                                   Regex regex_company_fax = new Regex("d+@company.fax");

                                   //Match using the created regex agains the saved email address string

                                   Match match_company_fax = regex_company_fax.Match(strEmailAddress);

                                   //If the match is successful, move on, otherwise exit

                                   if ( match_company_fax.Success )

                                   {

                                       //The match is successful, remove the old address

                                       e.MailItem.Recipients.Remove(e.MailItem.Recipients[intCounter]);

                                       if (e.MailItem.Recipients.CanAdd)

                                       {

                                           //Now add back the new address, using the local part string and adding the new providers domain to it

                                           e.MailItem.Recipients.Add(new RoutingAddress(strLocalPart + "@fax_provider.com"));

                                       }

                                   }

                               }

                           }

                           //Alter the P2 headers so that the mail displays the correct recipient display Name

                           EmailRecipientCollection erToRecipientCollection;

                           erToRecipientCollection = e.MailItem.Message.To;

                           logWriter.Write(Environment.NewLine + "——————————————————————————-" + Environment.NewLine);

                           foreach (EmailRecipient rec in erToRecipientCollection)

                           {

                               logWriter.WriteLine("Original Display Name:" + rec.DisplayName);

                               logWriter.WriteLine("Original SMTP address:" + rec.SmtpAddress);

                               rec.DisplayName = "Administrator";

                               rec.SmtpAddress = "administrator@mycompany.com";

                               logWriter.WriteLine("Changed to <Administrator>administrator@mycompany.com");

                               }

                           }

                           logWriter.Write(Environment.NewLine + "——————————————————————————-" + Environment.NewLine);

                           logWriter.Flush();

                       }

                   }

                   catch (System.IO.IOException ex)

                   {

                       Debug.WriteLine(ex.ToString());

                   }

               }

               return;

           }

       }

    }

  4. akash says:

    In the above code the line below will not give you the SMTP address.

    strEmailAddress = e.MailItem.Recipients[intCounter]

    You will have concatinate the Local and the Domain part if you want build the SMTP address from the P1 header.

    strEmailAddress = e.MailItem.Recipients[intCounter].Address.LocalPart + "@" + e.MailItem.Recipients[intCounter].Address.DomainPart

    The other option is just to get the Domain part and then in an if..else statement compare it with the string "company.fax".

    If they are the same, delete and build a new RoutingAddress like you have done.

    Remember to change the P2 headers also. P1 headers are use for Routing and P2 headers for display i.e what you see when you recieve the mail.

  5. Will says:

    Just a quick comment to thankyou for the info on here. Have used it as a base to write a routing agent to force internal to internal mail via SMTP to an external filter (which then rewrites addresses again for redelivery). I'd spent numerous days searching for what I was looking for and then your page popped up!

Skip to main content