Skip to content

DomainReroutingAgent

Tommaso Toniolo edited this page Apr 12, 2024 · 7 revisions

Description

This sample implements a RoutingAgent. This code executes when the message has been received from the Transport Front-End, put into the queue and is in the process of being categorized by the Hub Transport service.

Business Challenge

This agent has been written to intercept messages from specific address and re-route them via a separate Send Connector.

The organization had a need to avoid routing messages from mass-mailing domains via the default path (MX record of the recipient) and via Exchange Online, which at this time doesn't support High-Volume Email Traffic. The matching condition here is merely checking the P1 sender (in the SMTP envelope) however more complex matching condition based on any P1 attribute (in the SMTP envelope) or P2 attribute (message properties and headers) could be mixed and matched (i.e. only re-routing messages that contains a specific header as shown on HeaderAgent).

As such, this Transport Agent intercept messages from a given list of senders, and for messages to external recipients overwrite the routing domain to one for which there is a specific Send Connector; in this case ACS.TONIOLO.CLOUD.

In Exchange Server there is a Send Connector, in this case matching ACS.TONIOLO.CLOUD, configured to use Azure Communication Services Email as an upstream Smart Host.

Other SMTP compliant services can be used.

The agent offers two separate methods, one that handles the Routing Domain override, and another that manages the removal of empty message headers, given services like Azure Communication Services Email might not accept empty message headers.

Important note about capabilities

It must be noted that this Transport Agent is capable of intercepting messages regardless of the protocol used for generating the outbound message (Exchange Web Services, SMTP, Outlook, OWA, ActiveSync, Outlook, Mobile, etc.) and likewise will be able to process messages regardless of how the Exchange server received them (TLS-based connector, IP-based connector, SMTP submission, anonymous connector, basic authentication, pickup directory).

This, as such, can provide way to leverage specific egress or mass-mailing services, like Azure Communication Services, even from applications that cannot directly interact with it or that are not natively compatible, and transparently for the application owners and developers.

Initial Configuration

First you need to configure your Azure Communication Services or SMTP compliant service. For Azure Communication Services Email please refer to the official documentation [here](First thing you have to configure ACS as detailed on https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/email/send-email-smtp/smtp-authentication).

Once you've followed the article you shall have your username (in the format AzureCommunicationServicesResourceName|EntraApplicationID|EntraTenantID) and the password (the client secret for the registered application).

You then need you to create a new Send Connector and set smtp.azurecomm.net and the username/password retrieved at the previous step as shown in the following image. Note that the Exchange Admin Centre doesn't allow setting a target port (587, in this case) for the Send Connector.

image

At this point you need to log on to the Exchange Management Shell and update the newly create Send Connector to use port 587, that is used in Azure Communication Services (and other SMTP-compliant services) for client submission. The image below shows the Get- command to allow viewing all the properties and their configuration; use the Set-SendConnector to modify the "Port" attribute.

image

At this point all pre-requisites will be met and the Transport Agent will be able to re-route messages.

Runtime Configuration

This agent, due to the varying nature of overrides that might be time-dependant or business-dependant, doesn't rely on hard-coded configuration or text-file configuration, which might result into locks if they are getting edited or accessed by other software. As such, the configuration is stored in the Windows Registry under the "Current User", where the user is the Network Service, registry hive.

The "Current User" registry hive has been selected as the Network Service doesn't run as an elevated service, and therefore access to "Local Machine" was prohibited.

For the sake of the sample, if the configuration is missing, the first time the agent executes it will create the registry keys with the following sample values:

  • OverrideRoutingDomain-AgentEnabled = True; controls whether the routing domain override portion of the agent is active and processing email
  • OverrideRoutingDomain-DebugEnabled = True; controls whether debug output is written in the Event Log when the outing domain override portion of the agent executes
  • RemoveEmptyHeaders-AgentEnabled = True; controls whether the empty header removal portion of the agent is active and processing email
  • RemoveEmptyHeaders-DebugEnabled = True; controls whether debug output is written in the Event Log when the empty header removal portion of the agent executes
  • SendersToReroute = "[email protected]|acs.toniolo.cloud"; this tells the agent to override the routing domain to "acs.toniolo.cloud" for messages sent from "[email protected]". This is a Multi-String, or a List of String where every line entry correspond to a sender|overrideDomain mapping.
  • InternalDomains = "toniolo.cloud","totonibeta.onmicrosoft.com"; this tells the agent to treat messages sent to either "toniolo.cloud" or "totonibeta.onmicrosoft.com" as internal messages and therefore avoid overriding the rotuing domain. This is a Multi-String, or a List of String where every line entry correspond to a internalDomain. Note that the agent will check the domain for an exact match (no wild-card allowed).

image

Logging

This Transport Agent implements Event Logging for its operation. This has been chosen at it diminishes the chances of crashing the transport service in scenarios such as when a log file (if logging to text) could be locked by another process (i.e. anti-virus) or not accessible (i.e. missing permissions or non-existing path).

Event Logs would be recorded in the Application Event Log on the server; the agent would try to register a Source matching its name (DomainReroutingAgent), if this is not possible it will try to fall back on the DLL name (TransportAgents) and, in case that is not possible as only applications running elevated has the privilege to do so, will fall back to logging entries as "Application" which should be already present and registered.

If you want to make sure the Transport Agent is capable or logging events under its own, separate, source for a quicker identification of the relevant events, please create the source manually on an elevated PowerShell prompt.

This can be done with the following command.

New-EventLog -LogName Application -Source DomainReroutingAgent

Alternatively, if you want all different agents to log theri events on a single source, you can create the TransportAgents one as follows.

New-EventLog -LogName Application -Source TransportAgents

image

Installation

  • Copy the DLL to the server (i.e. F:\Transport Agents); Make sure the Exchange accounts have access to the folder
  • Install the transport agent

Install-TransportAgent -Name DomainReroutingAgent -TransportAgentFactory "TransportAgents.DomainReroutingAgent" -AssemblyPath "F:\Transport Agents\TransportAgents.dll"

  • Enable the chosen Transport Agent

Enable-TransportAgent DomainReroutingAgent

  • Exit from Exchange Management Shell
  • Restart the MSExchangeTransport service

Restart-Service MSExchangeTransport

Removal

  • Disable the agent

Disable-TransportAgent DomainReroutingAgent

  • Remove the agent

Uninstall-TransportAgent DomainReroutingAgent

  • Exit from Exchange Management Shell
  • Restart the MSExchangeTransport service

Restart-Service MSExchangeTransport