As this blog points out, it is sometimes necessary to control the generation of delivery status notifications, for example if the Exchange Server is responsible for multiple domains. In this case, Exchange uses the default SMTP domain as the sender for DSNs.
The blog entry briefly describes the necessary steps but it turns out that the entire process of registering the sink is poorly documented. Additionally diffculties arose because I wanted to implement the sink with managed code.
Also Microsoft offers an example on how to implement managed transport event sinks and also includes the necessary type libraries, this library lacks the necesssary interface definitions required for the OnSyncGenerateDSN event sink.
This implementation changes the domain for the DSN to the domain of the first recipient of the original mal.
The first step was to generate a type library which contains the interface definitions for the event sink. To do this, I modified the smtpevent.idl file, which is included in the Platform SDK. The modified version is attached to this article.
I compiled the modified smtpevent.idl with the MIDL compiler and created an interop assembly with the Type library import utility (tlbimp.exe).
Unfortunately, some of the methods are incorrectly marshalled, and thus, can't be used. So, the next step is to decompile the generated assembly with ildasm.exe, correct the definition of the methods and recompile the source with the ilasm.exe compiler. The modified interop dll is also attached to this article.
The implemention of the actual interface is the most easy part of the entire process. The only method that has to be implemented is the IDSNGenerationSink.OnSyncGenerateDSN:
1 public void OnSyncGenerateDSN(ISMTPServer pISMTPServer, IDSNSubmission pIDSNSubmission, IMailMsgProperties pIMsg, IMailMsgPropertyBag pIDSNProperties, IDSNRecipientIterator pIRecipIter)
2 {
3 StringBuilder sb;
4 IMailMsgRecipientsBase recipients;
5 string domain;
6
7 Trace.WriteLine("[DSNRewriter] OnSyncGenerateDSN called");
8 try
9 {
10 recipients = (IMailMsgRecipientsBase)pIMsg;
11
12 sb = new StringBuilder(512);
13 try
14 {
15 recipients.Item(0, (uint)_CAT_ADDRESS_TYPE.CAT_SMTP, 512, sb);
16 domain = sb.ToString();
17 domain = domain.Substring(domain.IndexOf("@") + 1);
18
19 Trace.WriteLine("[DSNRewriter] Domain of first recipient is: " + domain);
20
21 Trace.WriteLine("[DSNRewriter] Setting default domain to " + domain);
22 pIDSNProperties.PutStringA((uint)eDSNProperties.DSNPROP_SZ_DEFAULTDOMAIN, domain);
23 Trace.WriteLine("[DSNRewriter] Done");
24 }
25 catch (Exception ex)
26 {
27 Trace.WriteLine("[DSNRewriter] The following exceptions occured: " + ex.ToString());
28 }
29
30 }
31 catch (Exception ex)
32 {
33 Trace.WriteLine("[DSNRewriter] Failed. Exception: " + ex.ToString());
34 }
35 }
This implementation sets the domain for the DSN to the domain of the first recipient of the original message.
After the code has been compiled to an assembly it must be registered on the Exchange server with the regasm.exe utility.
The final step is the registration of the event sink within the store. Again, Microsoft offers a script which handles registration, unregistration and enumeration of SMTP event sinks. And again, this script lacks the functionality to register DSN related event sinks. A modified version of this script is also attached to this article.
To register the event sink run the modified smtpevent.vbs with the following parameters:
1 csript smtpevent.vbs /add 1 dsngenerationsink dsnrewriter dsnrewriter.dsnrewriter ehlo=*
This installs the event sink on the first SMTP virtual server.
To uninstall the event sink, run the following command:
1 cscript smtpevent.vbs /remove 1 dsngenerationsink dsnrewriter