|
Introduction
Junk e-mail is a common problem in the modern Internet. Mailboxes become flooded with e-mail not directly intended for users, and users are subjected to rude or disgusting messages. Many commercial solutions for this problem exist, however it is easy to create a custom solution to the problem using Microsoft Collaboration Data Objects (CDO) for Windows 2000, an application program interface (API) that is part of all versions of Windows 2000 Server.
SMTP Service
The Microsoft Simple Mail Transport Service (SMTP) is a component of Windows 2000 Server's Internet Information Services (IIS)5.0. The SMTP service is capable of generating two types of events, Protocol events and transport events. Protocol events are events relating to the transmission of raw data and transport events relate to outcomes of a protocol event, such as the delivery of e-mail or a newsgroup post. CDO has an easy-to-use interface to a subset of the SMTP service events. The event used for the spam filter is the OnArrival event.
OnArrival Event
The OnArrival event occurs when a message has been successfully received by the SMTP service and before the message has been written to the drop directory or processed by the mail server (such as Microsoft Exchange Server). It is possible for the message to be aborted, which is why this event is used for the spam filter. The header for the OnArrival event (when implemented in VBScript) is as follows:
Sub ISMTPOnArrival_OnArrival(ByVal oMsg, intEventStatus)
oMsg is a CDO Message object that is passed by value. It is possible to make changes to this object persistent by using methods of the Message class. intEventStatus is an integer result used by CDO to determine if any further event sinks need to be triggered after the current one is complete.
Spam Detection
Once the OnArrival event fires, the message object is available for scrutiny. In the example solution, the message subject and body are searched for a series of stopwords; if any are found, the message is rejected. An alternate implementation could compare the message FROM field against a list of domains known to be spam.
Spam Rejection
In the example solution spam rejection is a three-step process:
- Send a "rejection message" to the sender to let them know that the message was rejected, why it was rejected, and contact information in case the message was important.
- Log the rejection to a text file, recording the reason, relevant message header fields, and the body of the message.
- Abort the delivery of the message.
CDO is used to send the rejection message, as shown in the following code:
Dim oRejectionMessage
Set oRejectionMessage = CreateObject("CDO.Message")
oRejectionMessage.To = oMsg.From
oRejectionMessage.From = strPostmasterEmail
oRejectionMessage.Subject = "Mail rejected"
oRejectionMessage.TextBody = "Your message with the subject & _
""" & oMsg.Subject & """ addressed to " & oMsg.To & _
" was rejected by a content filter due to the presence " & _
"of the phrase or word `" & strStopWord & "`."
oRejectionMessage.Send
Set oRejectionMessage = Nothing 'release the object reference
A FileStream object (Scripting.FileSystemObject.TextStream) is used to append data to the log file:
' log the message
Dim oFS
Set oFS = CreateObject("Scripting.FileSystemObject")
Dim oLog
Set oLog = oFS.OpenTextFile("c:\spam.log", 8, True )
oLog.Write "-----------------------------------------------" & vbCrLf
oLog.Write "strStopWord `" & strStopWord & "` detected." & vbCrLf
oLog.Write "From: " & oMsg.From & vbCrLf
oLog.Write "To: " & oMsg.To & vbCrLf
oLog.Write "Subject: " & oMsg.Subject & vbCrLf & "Date: & _
" & now() & vbCrLf & vbCrLf
oLog.Write oMsg.TextBody & vbCrLf & vbCrLf
oLog.Close
Messages are aborted by altering the MessageStatus envelope field. This field is retrieved from the EnvelopeFields collection of the message. The message is then saved back to the data store using the CDO Message.DataSource.Save() command.
Dim oFlds
Set oFlds = oMsg.EnvelopeFields
oFlds("http://schemas.microsoft.com/cdo/smtpenvelope/messagestatus") = 3 ' bad mail, abort delivery
oFlds.Update ' must call update to commit changes to fields
oMsg.DataSource.Save()' save the message to the message store
Finally, the event's EventStatus parameter is set to 1 to indicate that no further event sinks should be triggered. (It is possible to register multiple event sinks for a given event.)
Installation
To register the event, the smtpreg.vbs script is used. This script is available from Microsoft by downloading the Platform SDK section on CDO for Windows 2000. (See http://www.microsoft.com/msdownload/platformsdk/sdkupdate/)
This is a two-step process. First, an event sink is created using the following command:
cscript smtpreg.vbs /add 1 onarrival SMTPScriptingHost CDO.SS_SMTPOnArrivalSink "mail from=*"
This command adds an OnArrival event sink to the SMTP service. The sink is registered in location 1, and so will be processed first (other event sinks may be registered by specifying a different location). While the SMTPScriptingHost indicates that the event sink will be a VBS file, event sinks may also be created using C++ or other compiled languages. The keyword CDO.SS_SMTPOnArrivalSink specifies that class will handle the sink, and "mail from=*" specifies that mail from anyone will be sent to the sink.
The second command used to register the sink is:
cscript smtpreg.vbs /setprop 1 onarrival SMTPScriptingHost Sink ScriptName "c:\path\to\spamfilter.vbs"
This command sets the ScriptName property of the previously declared event sink to our VBS file. This file will be interpreted when the event fires.
The complete source code for the spam filter is available for download here.
Testing
To test the event sink, install it on a server equipped with Microsoft Exchange 2000 or another mail system that uses CDO, and send a message with one of the specified stopwords in it. This should result in the generation of a rejection message, a log notation, and the abortion of the message's delivery. A variation of this script has been used by the author to filter content on a medium-sized Web server (providing virtual Web and e-mail hosting for 50 domains and a few hundred users); no noticeable performance drop occurred after the script was installed, and junk e-mail volume decreased dramatically.
Conclusion
CDO for Windows 2000 is a powerful API that can be used to create a content filter for e-mail. The filter may be implemented using a compiled language such as C++ (by creating a registered ATL COM object) or by using Visual Basic Scripting Edition to write an event handler.
About the Author
George Walker is a computer system technologist in Victoria, British Columbia, Canada. He is the senior systems analyst at Sage Internet Solutions, Ltd. and a Director of E-Scape Systems, Inc. He may be reached by e-mail at george@escapesystems.com.
References
Platform SDK: CDO for Windows 2000 -- Supported Transport Events with CDO
http://msdn.microsoft.com/library/en-us/cdosys/html/_cdosys_supported_transport_events_with_cdo.asp
Microsoft Windows 2000 SMTP Service Events by Microsoft's David Lemson.
This April 2000 document gives system administrators, project managers, and developers an overview of the Microsoft Windows 2000 SMTP service events.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsmtps/html/transportevents.asp
Platform SDK: CDO for Windows 2000 -- Registering Script Sink Bindings
http://msdn.microsoft.com/library/en-us/cdosys/html/_cdosys_registering_script_sink_bindings.asp
|