Microsoft Windows 2000 SMTP Service Events

 

David Lemson
Microsoft Corporation

April 2000

Summary: This document gives system administrators, project managers, and developers an overview of the Microsoft Windows 2000 Simple Mail Transfer Protocol (SMTP) service events. It discusses how the protocol and transport event architecture in Windows 2000 relates to Microsoft Exchange 2000 Server, why we have events, and how we can use events to write powerful server-side applications. (12 printed pages)

Contents

Introduction
Why Have Events?
Events Overview
Protocol Events
Transport Events
CDO_OnArrival Event
Conclusion

Introduction

So why are we talking about Windows® 2000 in an Exchange 2000 document? Exchange 2000 doesn't have its own Simple Mail Transfer Protocol (SMTP) protocol and transport events; they are part of Windows 2000. The SMTP service is a component of Internet Information Services (IIS) 5.0 and is shipped as part of all versions of Windows 2000. Because the SMTP service has many powerful queuing features, Exchange 2000 depends on it. Simply put, you can install Windows 2000 without installing Exchange 2000, but you must have the Windows 2000 SMTP service installed before you can install Exchange 2000.

Why Have Events?

Exchange 2000 is built on the Windows 2000 SMTP transport event architecture. This architecture allows you to customize message flow and add custom actions as messages flow between servers. Additionally, protocol events can be used to modify the SMTP protocol to add new Extended SMTP (ESMTP) commands, or even to change the action of existing ones. By extending the SMTP service you can develop many useful applications, such as:

Protocol events:

  • Perform billing and charge-back computations based on the number and length of connections.
  • Monitor systems through SMTP by implementing new ESMTP commands.

Transport events:

  • Forward all mail for a domain to a mailbox.
  • Add a disclaimer to the end of each message that leaves your network.
  • Scan all incoming mail from the Internet for keywords, subjects, and attachment filenames.
  • Check for viruses.
  • Check for spam (unsolicited commercial e-mail).

You can also use the SMTP service as an Internet mail relay agent to protect internal mail systems from attacks on the Internet. The computer running Windows 2000 server can be locked so that only connections to port 25 are allowed in from the Internet by either server or border firewall filtering.

The Windows 2000 SMTP service is designed to be flexible enough for you to build a full-featured mail system on top of it. Its event technology allows you to create applications that range from a simple mail filter to a full mail system like Exchange 2000 Server. A major benefit to this technology is that third-party enhancements can easily be installed on any Windows 2000 SMTP service or on any mail system that uses Windows 2000 SMTP service, such as Exchange 2000 Server.

Events Overview

An SMTP service event is described as "the occurrence of some activity within the service, such as the transmission or arrival of an SMTP protocol command, or the submission of a message into the SMTP service transport component." There are two main categories of SMTP service events: protocol events and transport events. Protocol events affect the SMTP communication between the client and the server by modifying inbound and outbound commands and responses to those commands. Transport events occur when messages flow through the SMTP core transport system, whether the message is sent to another machine, or delivered locally.

Figure 1 shows the scope of both event types and how they relate to one another.

ms998608.transportevents1(en-us,MSDN.10).gif

Figure 1. Protocol events and transport events

The default Windows 2000 protocol and transport events are only accessible by writing Component Object Model (COM) objects in Microsoft Visual C++®. These events are fast, require no extra processing, and offer access to the lowest-level properties of the messages; however, they are more complex to write. For smaller jobs that don't require high performance, you can use the CDO_OnArrival event, which can be written using Microsoft Visual Basic®, Scripting Edition (VBScript).

The following steps provide a high-level look at what happens during the SMTP protocol as a mail message travels through the system to its destination point:

  1. Protocol events. A mail message is sent via SMTP across a network to a Windows 2000 SMTP service on a server. At this point in our scenario you would use protocol events to control what happens as the mail message travels from server to server. These events are specific to SMTP and allow you to modify the behavior of the protocol stack. There are three main event categories here: In, Out, and Server Response.

  2. Transport events: OnSubmission/OnArrival. Once the mail message has arrived at the server (either next hop or final destination), the transport events take over. As the mail message arrives at the Windows 2000 SMTP service it triggers an OnSubmission event. This event causes the dispatcher to check a database (metabase) to see if there are any sinks associated with the mail message. The database notifies the dispatcher of all the event sinks that are registered and the dispatcher in turn notifies those events that the message is coming, what sinks to run, and when to run them. The processing order depends upon the sinks' priority as registered in the database. These sinks can be written to run asynchronously so that other messages can still be processed in the event that one message is held up by a lengthy process. The sinks can also be written so that the dispatcher can persist an instance of a sink for future use. This feature increases the performance sinks that can be called multiple times.

    An alternative method of handling an OnSubmission event is to use the CDO_OnArrival event. With this event, a sink can be written in any Collaboration Data Objects (CDO)-capable language, including Visual Basic and VBScript. The CDO_OnArrival event is a wrapper around the transport OnSubmission event that provides a handle to the message in the CDO message format. The major benefit to using CDO_OnArrival is that the CDO message object interface has many useful methods, such as the parsing of MIME and RFC 822 headers. The major drawback is that the CDO interface adds significant overhead and is synchronous. The CDO_OnArrival event is most appropriate for sinks that are run on very few messages. The OnSubmission/OnArrival event is the only event that offers a CDO interface and the ability to write event sinks in Visual Basic or VBScript. The OnSubmission event is the place to add new recipients to the message.

    Figure 2 illustrates the transport events within the SMTP service.

    ms998608.transportevents2(en-us,MSDN.10).gif

Figure 2. Transport events within the SMTP service

  1. Transport event: PreCategorizer. This event is similar to the OnSubmission event, except that it offers no CDO version. At this event, all recipients should have already been added (during the OnSubmission event) and the final unresolved recipient list should be on the message. In particular, any distribution lists that are recipients will not have been expanded.
  2. Transport event: Categorizer. The purpose of the Categorizer is to perform directory lookups to resolve the sender and recipients. Categorizer event sinks can add extra attributes to be looked up with no performance cost, or can modify the way that attributes are looked up in the directory. As mentioned earlier, a default categorizer sink (that is disabled by default, but can be enabled via the Internet System Manager UI) is delivered with Windows 2000, or you can create you own custom sink in Visual C++. There is no CDO version of the Categorizer event.
  3. Transport event: PostCategorizer. In the PostCategorizer event, the distribution lists have been expanded and the actual recipients are listed, so you can count the people who will get a copy of the message and use that number for recipient limit checking, for example. There is no default sink here but you can create custom sinks in Visual C++ that can be run on the mail message. There is no CDO version of the PostCategorizer event.
  4. Transport event: Routing. Windows 2000 SMTP service sends messages point-to-point to the final recipient destination host by default. There is no routing event sink installed by default with Windows 2000, and there is no CDO version of this event. The routing sink installed with Exchange 2000 determines where the mail message is ultimately bound, and directs the message toward that location via configured connectors. If the message is heading for another server, the routing sink will recalculate the next hop and direct the mail message toward that next hop.
  5. Store driver events. These events handle how the contents of messages are persisted. There are two main store driver events: One handles messages that come in via SMTP and need to be queued, and the other handles messages that are destined for local delivery on the server. The default event sinks installed with the Windows 2000 SMTP service store messages in file system directories. When Exchange 2000 is installed, the latter store driver is replaced with one that directs local delivered messages to the Exchange 2000 Web Storage System.
  6. Protocol events. If the mail message is intended to go on to another system, the SMTP protocol events take over again and the mail message goes through this process again at the next hop, continuing until it reaches its final local storage destination.

Protocol Events

Protocol Event Rules

A rule is a string identifying a protocol filter for the sink binding. The dispatcher uses the rule as a condition (or set of conditions) under which the sink is to be notified, such as:

  • Limit event calls to certain commands.

The rule is the command you want to hook (either send or respond to) such as DATA. For example,

  • To hook MAIL FROM: hook "MAIL".
  • To hook RCPT TO: hook "RCPT".

You put the command you want to hook in the "Rule" field.

Protocol Event Contexts

A context is an object that contains information about where you are in the SMTP protocol conversation. It has methods to get or set the current place in the protocol state. The following are the three contexts that you can call from your sink.

  • ISmtpInCommandContext is the command that was received.
  • ISmtpOutCommandContext is the command to be sent out.
  • ISmtpServerResponseContext is the response to a command previously sent.

Protocol Event Sinks

Each event sink is fired based on being in either the inbound or outbound category.

Inbound:

  • OnInboundCommand
  • OnServerResponse

Outbound:

Outbound Protocol commands are built-in as PRIO DEFAULT priority and can be added before, in place of, or after commands. You can override the built-in commands by registering your commands at a higher or lower priority to get them to fire when you want.

  • Session Start: EHLO
  • Message Start: MAIL FROM:
  • Per Recipient: RCPT TO:
  • Before Data: DATA
  • Session End: QUIT

Protocol Event Sink Interfaces

An event sink implements one or more of the following event sink interfaces.

  • ISmtpInCommandSink is called when an inbound SMTP command is received.
  • ISmtpOutCommandSink is called when an outbound SMTP command is to be sent.
  • ISmtpServerResponseSink is called when the server responds to previously sent commands.

Transport Events

You can use transport events to add restrictions, redirect messages to other servers, change the way messages flow, add text to a message body, scan the body, and change any message property.

Transport Events Rules

The definition of rules for protocol events also applies to transport events.

  • Limit event call to certain messages. There are three ways to do this:
    • Filter on Mail From=
    • Filter on Rcpt to=
    • Filter on EHLO=
  • Case-insensitive.
  • Available to OnSubmission, PreCategorizer, and PostCategorizer events.

Note You can put more than one rule on an event; in that case the rules are combined using logical OR.

If a rule is placed on a transport event, it will only fire if the message entered the system via the SMTP protocol or the SMTP service "Pickup" directory. Messages submitted from Microsoft Outlook® clients via Exchange 2000 will not cause an event to fire if there is any rule on the event.

Transport Event Sinks

This topic presents transport event sinks grouped by category.

Advanced Queue (AQ)

  • IMailTransportSubmission event
    • OnSubmission event exposed by CDO.
    • Supports asynchronous notification.
    • Recipients should be added here before categorizer-related events are expanded.
  • IMailTransportOnPreCategorize event
    • Supports asynchronous notification.
    • Should be used to check the recipient list.
  • IMailTransportOnPostCategorize event
    • Distribution Lists/Groups will have been expanded: Actual recipients will be listed.
    • Supports asynchronous notification.
  • IMailTransportRouterReset event
    • Implemented by the system.
    • Routing engine calls this event to cause Advance Queue to recompute the next hop for all queues.
    • Queues are recomputed by calling the routing engine.

Categorizer Sinks

  • Default categorizer resolves LDAP client against Active Directory™, if enabled.
  • Features of a categorizer sink include:
    • Can look up attributes with almost no performance cost
    • Can replace default LDAP lookup engine
  • More complicated than PreCat/PostCat sinks, and also more powerful.

Routing Sinks

  • IMessageRouter. Use this one if you want to implement your own rules for routing messages (as opposed to point-to-point, which is the default with the SMTP service).
    • Sink implements GetNextHop method.
    • Major routing engine interface.
    • Input: destination address.
    • Output: next hop address.
  • IMailTransportSetRouterReset. Causes the routing system to go into the state of "need to calculate new path."
    • Called by SMTP or MTA when a link is down to request computation of new path.
    • Will later call Advanced Queuing by IMailTransportRouterReset to recompute the next hop for all queues.
  • IMailTransportRouterSetLinkState. It is possible to implement scheduled connections using this interface.
    • Allows a routing sink to set link state.
    • Each link in Advanced Queuing has a scheduled ID.
    • Each link can be set to a specific link state: pending scheduled connection, pending retry, force connection now, or connected even if no messages.
  • IMessageRouterLinkStateNotification. When a system has tried to connect to a remote server and the connection fails, the following occurs:
    • Routing sink implements this interface.
    • Sink implements system calls this function when the link state changes.
    • Exchange 2000 routing engine uses this to mark a connector as "up" or "down" in internal link state table.

Subsequent Sinks

In your sinks you may specify that subsequent sinks will or will not fire. The sink indicates the behavior via the return code.

  • Sink returns S_OK. Other sinks at same or lower priority are called.
  • Sink returns S_FALSE. Other sinks at same or lower priority are not called.

This allows you to override the behavior of the sinks that were supplied with Exchange 2000.

Note If the sink returns S_PENDING to indicate that it will issue an asynchronous callback, the S_OK or S_FALSE is passed to IMailTransportNotify to indicate whether or not subsequent sinks should be called.

These are not transport or protocol events, but ancillary functions that you call or implement in certain situations.

  • IMailTransportNotify. This event is called when you have finished processing a message. The sink may return asynchronously, returning MAIL_TRANSPORT_S_PENDING, to notify the dispatcher that it will continue processing on another thread. That message will be halted and the dispatcher will wait for a callback by this function. The dispatcher will allow other messages to be processed.
  • IsCachable. If implemented, this indicates to the dispatcher that it can call your sink multiple times in a row without having to tear it down. If not defined, the sink instance is deleted after exit. If this method is not implemented, indicating that the sink must not be called multiple times, CoCreateInstance( ) must be called for each event raised which will reduce performance. In order to implement this, the sink must be re-entrant/thread-safe. There can be no shared data without proper locking. This method can be implemented by both transport events and protocol events.

Transport Event Sink Interfaces

The following are the basic steps for creating a transport event sink interface. For more detailed information please refer to the MSDN Library.

  1. Implement the sink interface method by using COM. This is important to learn. Most sinks use the Active Template Library (ATL) to make this simple.
  2. Register the COM interface of sink in registry. This is standard COM practice.
  3. Register the interface of the new sink in the event binding database in the registry.
  4. Submit a message and see the sink run.

CDO_OnArrival Event

As mentioned earlier, the CDO_OnArrival event is a wrapper around the transport OnSubmission event that provides a handle to the messages in the CDO message format. The major benefit is that the Message object interface has many useful methods, such as the parsing of MIME and RFC 822 headers. The major drawback is that the CDO interface adds significant overhead, and is synchronous. The CDO_OnArrival event is most appropriate for sinks that are run on very few messages. This is the only piece that is easily programmable using a high-level scripting language like VBScript.

Features of a CDO_OnArrival event sink include:

  • Scriptable.
  • Dual interface.
  • Higher level.
  • VBScript, Visual C, Visual C++, Visual Basic.
  • Only one event accessible through the interface.
  • Passes a CDO message object that gives easy access to message fields, attachments, body parts, and so on.

To use, implement ISmtpOnArrival. This CDO event is called by transport:ISmtpOnArrival.

The VBScript shell of an event sink is the following:

<SCRIPT LANGUAGE="VBScript">
Sub ISMTPOnArrival_OnArrival( ByVal Msg, Status)
  ' sink code here
End Sub
</SCRIPT>

The message takes in, by value, a CDO message object ('Msg' in the preceding code example) and it returns an event status object ('Status' in the preceding code example). You have the option of setting the status.

The value of the status object determines if subsequent sinks (lower priority) will run by returning one of the CDOEventStatus enumeration values.

  • CdoRunNextSink 0
  • CdoSkipRemainingSinks 1

See the CDO for Windows 2000 section of the Microsoft Platform SDK for full documentation of the CDO Message object properties and methods.

The following steps show how to create a CDO sink.

  1. Implement CDO_InArrival( ) function.
  2. Register sink in metabase using CDO.SS_SmtpOnArrivalSink class.
  3. Submit a message and see the sink run.

Conclusion

This document gives an overview of the Windows 2000 SMTP service events, discusses how the protocol and transport event architecture in Windows 2000 relates to Microsoft Exchange 2000, why we have events, and how we can use them to write powerful server-side applications. It gives only enough information to help you understand this very complicated subject without going into details. For further details, including in-depth information that will allow you to successfully build your own applications, please refer to the Platform SDK in the MSDN library.