// This is a custom transport agent for sender based re-routing, created by Martin Tuescher
// As it is here, it needs a Send Connector configured to route mails to override domains that has equal or lower costs than the Default Send Connector for "*".
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Configuration.Install;
using System.ComponentModel;
using Microsoft.Win32;
using Microsoft.Exchange.Data.Transport;
using Microsoft.Exchange.Data.Transport.Email;
using Microsoft.Exchange.Data.Transport.Smtp;
using Microsoft.Exchange.Data.Transport.Routing;
using Microsoft.Exchange.Data.Common;
namespace Microsoft.Exchange.SBR
{
///
/// Run when installing the assembly, this function sets up the required
/// application event logging registry keys
///
[RunInstaller(true)]
public class SbrRoutingAgentEventLogInstaller : Installer
{
private EventLogInstaller SbrEventLogInstaller;
public SbrRoutingAgentEventLogInstaller()
{
SbrEventLogInstaller = new EventLogInstaller(); // Create an instance of an EventLogInstaller.
SbrEventLogInstaller.Source = "Microsoft.Exchange.SBR"; // Set the source name of the event log.
SbrEventLogInstaller.Log = "Application"; // Set the event log that the source writes entries to.
Installers.Add(SbrEventLogInstaller); // Add routingeventLogInstaller to the Installer collection.
RegistryKey newRegKey = Registry.LocalMachine.CreateSubKey("System\\CurrentControlSet\\Services\\MSExchangeSbrAgent\\Diagnostics");
newRegKey.SetValue("General", 0);
}
}
public sealed class SbrRoutingAgentFactory : RoutingAgentFactory
{
public override RoutingAgent CreateAgent(SmtpServer server)
{
RoutingAgent myAgent = new OwnRoutingAgent();
return myAgent;
}
}
public class OwnRoutingAgent : RoutingAgent
{
private int eventLogLevel = -1; // Don't log if the proper keys aren't setup because this could cause problems later.
private RegistryKey eventLogRegKey; // This is a member in case we want to add registry monitoring
private Dictionary _RoutingTable;
private String OverrideSettingsFileName = Environment.GetEnvironmentVariable("ExchangeInstallPath", EnvironmentVariableTarget.Machine) + @"TransportRoles\agents\Custom\Microsoft.Exchange.SBR.OverrideSettings.config";
private Dictionary _InternalDomains;
private String InternalDomainsFileName = Environment.GetEnvironmentVariable("ExchangeInstallPath", EnvironmentVariableTarget.Machine) + @"TransportRoles\agents\Custom\Microsoft.Exchange.SBR.InternalDomains.config";
private String[] _IgnoreAuthAsValues;
private String IgnoreAuthAsFileName = Environment.GetEnvironmentVariable("ExchangeInstallPath", EnvironmentVariableTarget.Machine) + @"TransportRoles\agents\Custom\Microsoft.Exchange.SBR.IgnoreAuthAs.config";
public OwnRoutingAgent()
{
this.eventLogRegKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\services\\MSExchangeSbrAgent\\Diagnostics"); // Read EventLogLevel from Registry
if (this.eventLogRegKey != null)
{
if (eventLogRegKey.GetValue("General") != null)
{
this.eventLogLevel = ((int)eventLogRegKey.GetValue("General"));
if (eventLogLevel >= 6) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Event logging set to level " + this.eventLogLevel.ToString() + ".");
}
}
//subscribe to different events
base.OnResolvedMessage += new ResolvedMessageEventHandler(OwnRoutingAgent_OnResolvedMessage);
_IgnoreAuthAsValues = File
.ReadAllLines(IgnoreAuthAsFileName)
.Where(line => !String.IsNullOrEmpty(line))
.Where(line => !line.StartsWith("#"))
.Select(line => line.ToLower())
.ToArray(); // assumes line has 1 element, no error check
_InternalDomains = File
.ReadAllLines(InternalDomainsFileName)
.Where(line => !String.IsNullOrEmpty(line))
.Where(line => !line.StartsWith("#"))
.Select(line => line.ToLower())
.Select(line => line.Split(';'))
.ToDictionary(items => items[0], items => items[1]); // assumes line has 2 elements, no error check
_RoutingTable = File
.ReadAllLines(OverrideSettingsFileName)
.Where(line => !String.IsNullOrEmpty(line))
.Where(line => !line.StartsWith("#"))
.Select(line => line.ToLower())
.Select(line => line.Split(';'))
.ToDictionary(items => items[0], items => items[1]); // assumes line has 2 elements, no error check
}
private void OwnRoutingAgent_OnResolvedMessage(ResolvedMessageEventSource source, QueuedMessageEventArgs e)
{
try
{
var routingOverrideDomainPart = "";
var senderFullEmailAddress = e.MailItem.FromAddress.ToString().ToLower(); // get full sender email address
var senderOnlyDomainPart = e.MailItem.FromAddress.DomainPart.ToLower(); // get domain part of sender email address
var emailMessageId = e.MailItem.Message.MessageId.ToString();
var emailMessageSubject = e.MailItem.Message.Subject.ToString();
Microsoft.Exchange.Data.Mime.Header emailMessageOrgAuthAs;
emailMessageOrgAuthAs = e.MailItem.Message.MimeDocument.RootPart.Headers.FindFirst("X-MS-Exchange-Organization-AuthAs");
if (eventLogLevel >= 4) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Entering OnResolved for message " + emailMessageId + " (\"" + emailMessageSubject + "\").", EventLogEntryType.Information, 1);
if (!_IgnoreAuthAsValues.Contains(emailMessageOrgAuthAs.Value.ToString().ToLower()))
{
// X-MS-Exchange-Organization-AuthAs value not found in ignore table (typically "Internal") so we start processing the mail
if (eventLogLevel >= 5) EventLog.WriteEntry("Microsoft.Exchange.SBR", "\"X-MS-Exchange-Organization-AuthAs\" header for message " + emailMessageId + " (\"" + emailMessageSubject + "\"):\n" + emailMessageOrgAuthAs.Value.ToString().ToLower(), EventLogEntryType.Information, 1);
if (!_RoutingTable.ContainsKey(senderOnlyDomainPart) && !_RoutingTable.ContainsKey(senderFullEmailAddress))
{
// sender email domain or address not found in rerouting table, quit
if (eventLogLevel >= 4) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Exiting OnResolved for message " + emailMessageId + " (\"" + emailMessageSubject + "\"):\nSender email domain or address not found in rerouting table.\nSBR agent is not processing this mail.", EventLogEntryType.Information, 8);
return;
}
else
{
// sender email domain or address found in rerouting table
int rerouteExternalCount = 0;
int rerouteInternalCount = 0;
int noRerouteCount = 0;
int totalCount = 0;
if (_RoutingTable.ContainsKey(senderOnlyDomainPart))
{
// sender email domain is in rerouting table
if (eventLogLevel >= 3) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Sender email domain " + senderOnlyDomainPart + " for message " + emailMessageId + " (\"" + emailMessageSubject + "\") is in rerouting table.", EventLogEntryType.Information, 2);
routingOverrideDomainPart = _RoutingTable[senderOnlyDomainPart];
}
if (_RoutingTable.ContainsKey(senderFullEmailAddress))
{
// sender email address is in rerouting table
if (eventLogLevel >= 3) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Sender email address " + senderFullEmailAddress + " for message " + emailMessageId + " (\"" + emailMessageSubject + "\") is in rerouting table.", EventLogEntryType.Information, 2);
routingOverrideDomainPart = _RoutingTable[senderFullEmailAddress];
}
var myRoutingOverride = new RoutingDomain(routingOverrideDomainPart);
foreach (EnvelopeRecipient recp in e.MailItem.Recipients)
{
if (_InternalDomains.ContainsKey(recp.Address.DomainPart.ToLower()))
{
// recipient domain is defined internal, now we have to look if GroupID is equal or not
if (_InternalDomains[senderOnlyDomainPart].Equals(_InternalDomains[recp.Address.DomainPart.ToLower()], StringComparison.OrdinalIgnoreCase))
{
// No routing override, GroupID is equal
if (eventLogLevel >= 2) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Not overriding recipient " + recp.Address.ToString() + " on " + emailMessageId + " (\"" + emailMessageSubject + "\"):\nGroupID " + _InternalDomains[senderOnlyDomainPart].ToString().ToLower() + " is equal.", EventLogEntryType.Information, 4);
noRerouteCount++;
}
else
{
// Routing override, GroupID is not equal
if (eventLogLevel >= 2) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Overriding recipient " + recp.Address.ToString() + " on " + emailMessageId + " (\"" + emailMessageSubject + "\"):\nRecipient's GroupID " + _InternalDomains[recp.Address.DomainPart.ToLower()].ToString().ToLower() + " is not equal to sender's GroupID " + _InternalDomains[senderOnlyDomainPart].ToString().ToLower() + ".", EventLogEntryType.Information, 4);
RoutingOverride newRoute = new RoutingOverride(myRoutingOverride, DeliveryQueueDomain.UseOverrideDomain);
source.SetRoutingOverride(recp, newRoute);
rerouteInternalCount++;
}
}
else
{
// recipient domain is not defined internal, so it must be external and routing overridden...
if (eventLogLevel >= 2) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Overriding recipient " + recp.Address.ToString() + " on " + emailMessageId + " (\"" + emailMessageSubject + "\"):\nRecipient not defined internal.", EventLogEntryType.Information, 4);
RoutingOverride newRoute = new RoutingOverride(myRoutingOverride, DeliveryQueueDomain.UseOverrideDomain);
source.SetRoutingOverride(recp, newRoute);
rerouteExternalCount++;
}
totalCount++;
}
if (eventLogLevel >= 1) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Statistics for message " + emailMessageId + " (\"" + emailMessageSubject + "\"):\n" + totalCount.ToString() + " recipients checked for rerouting.\n" + rerouteExternalCount.ToString() + " external recipients were rerouted.\n" + rerouteInternalCount.ToString() + " internal recipients were rerouted because GroupIDs were not equal.\n" + noRerouteCount.ToString() + " internal recipients were not rerouted because GroupIDs were equal.", EventLogEntryType.Information, 5);
if (eventLogLevel >= 4) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Exiting OnResolved for message " + emailMessageId + " (\"" + emailMessageSubject + "\").", EventLogEntryType.Information, 8);
}
}
else
{
// X-MS-Exchange-Organization-AuthAs value found in ignore table
if (eventLogLevel >= 4) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Exiting OnResolved for message " + emailMessageId + " (\"" + emailMessageSubject + "\"):\nX-MS-Exchange-Organization-AuthAs header \"" + emailMessageOrgAuthAs.Value.ToString().ToLower() + "\" found in ignore table.\nSBR agent is not processing this mail.", EventLogEntryType.Information, 8);
return;
}
}
catch (Exception except)
{
if (eventLogLevel >= 3) EventLog.WriteEntry("Microsoft.Exchange.SBR", "Exception (OnResolved): " + except.Message + "\n" + except.StackTrace, EventLogEntryType.Information, 10);
}
}
}
}