Become a Columnist Microsoft Exchange Site Microsoft Support SiteMSDN Exchange Site

   

Subscribe to OutlookExchange
Anderson Patricio
Ann Mc Donough
Bob Spurzem
Brian Veal
Catherine Creary
Cherry Beado
Colin Janssen
Collins Timothy Mutesaria
Drew Nicholson
Fred Volking
Glen Scales
Goran Husman
Guy Thomas
Henrik Walther
Jason Sherry
Jayme Bowers
John Young
Joyce Tang
Justin Braun
Konstantin Zheludev
Kristina Waters
Kuang Zhang
Mahmoud Magdy
Martin Tuip
Michael Dong
Michele Deo
Mitch Tulloch
Nicolas Blank
Pavel Nagaev
Ragnar Harper
Ricardo Silva
Richard Wakeman
Russ Iuliano
Santhosh Hanumanthappa
Steve Bryant
Steve Craig
Todd Walker
Tracey J. Rosenblath
 
   

Building a List of Spam Hosts

Page 1 | Page 2 | Page 3 | Page 4 | Page 5

Reading in data from NewSPAMHosts.log

The other option to create contacts in the AD is via an input file, by default the SMTPSPAMFilter.vbs script logs information to NewSPAMHosts.log that can be used to create contacts with the same properties as those created via the folder method above.

First we need to bind to the file that contains the list of host.  Then we will start a Do While loop that will continue until each line of the text file has been read in.  With each line we parse the data, which should be separated with tabs, Chr(9), to get the sending host IP, claimed DNS host name, and the message header.  The last two fields aren?t required but will be used if they exist.  We then get the class C address of the sending host by calling the custom GetClassC function which replaces the last IP bit with a 0, zero. 

Then we try to bind to the contact in the AD, since there is no point in trying to create a new contact and looking up information on the IP if a contact already exists.  First the script attempts to bind to a contact with the cn of the sending host IP.  If this fails it then tries the class C address.  If both fails then the rest of the script will continue, otherwise the next line is read in.  The 80072030 is the string and hex equivalent of the integer error code for ?object not found?.

The first check is to see if the sending IP has been blacklisted.  If it hasn?t been blacklisted we then attempt to look up equivalent DNS name with the NSLookUp function.  If the DNS lookup fails it is assumed the host should be blocked and the variables are set to match the format used in the Sub above.  Otherwise, we call the CheckDomainName function to return the HostDNS name, HostIP to be blocked, and sn to be used for the contact.

Next we reformat the header so each piece of header data will be on a different line by using the Replace function.  This is needed because SMTPSPAMFilter.vbs puts all the header information on one line separated by ?^^^?.  We then check to see if HostDNS is blank, if it is blank the script assume the host is valid and a contact shouldn?t be created for it.  If the HostDNS does contain data we then check to see if the contact should be created using the class C address, if so the HostIP is changed to a class C address.  Then HostDNS is checked to see if it contains ?Unknown?, which indicates the host was not a dynamic or know valid host, and appends the claimed DNS name to HostDNS.  Last CreateContact is called with the variable needed to set the fields on the contact in the same way as the ReadInItems sub.  After the contact is create the loop continues to the next line in the input file

Sub LoadHosts (SpamHosts,objSpamContactsOU)
  givenName = "File"
? Used when creating the contact
  Set objInputFile = objFS.OpenTextFile (SpamHosts, 1, True)
  Do While ObjInputFile.AtEndOfStream <> True
    ReadText = Trim(ObjInputFile.ReadLine)
    RealServerIP = GetField(ReadText,Chr(9))
    ClaimedHostDNS = GetField(ExtraData,Chr(9))
    MsgHeader = GetField(ExtraData,Chr(9))
    SendingHostClassC = GetClassC(RealServerIP)
    SpamContactPath = "LDAP://" & TargetDC & "/" & "cn=" & RealServerIP & "," & _
      SpamContactsOU & "," & DomainDN
    On Error Resume Next
    Set objSpamContact = GetObject (SpamContactPath)
  
 ' If the error was object not found try to bind to the contact with the
    ' class C address
    If CStr(Hex(Err.Number)) = "80072030" Then
      SpamContactPath = "LDAP://" & TargetDC & "/" & "cn=" & SendingHostClassC & _
        "," & SpamContactsOU & "," & DomainDN
      Set objSpamContact = GetObject (SpamContactPath)
    End If
    If CStr(Hex(Err.Number)) = "80072030" Then
? If still not found check out the host
      On Error GoTo 0
      If BlackListed (RealServerIP,BlackListedBy) Then
        HostDNS = "Blacklisted by [" & BlackListedBy & "] Claimed: " & ClaimedHostDNS
        sn = "Blacklisted"
      Else
        RealDNS = NSLookUp(RealServerIP,"Name:")
       
? Assume the host is a spamer and set the fields used by the contacts
        If RealDNS = "Invalid" Then
          HostDNS = ClaimedHostDNS
          RealDNS = "Failed NSLookup"
          sn = "Failed NSLookup"
          HostDNS = "Failed NSLookup, claimed: [" & ClaimedHostDNS & "]"
        Else
          ' Returns blank data if host is well known host or non-dynamic part
          ' of the hostif it is a dial-up host
          HostDNS = CheckDomainName (RealServerIP,HostIP,RealDNS,sn)
        End If
      End If
' Check for blacklisting
     
' ^^^ is used as a seperator in NewSPAMHosts.log for the message header
      If InStr(MsgHeader,"^^^") > 0 Then
        MsgHeader = Replace(MsgHeader,"^^^",VbCrLf)
      End If
      If HostDNS <> "" Then
        If UseClassC Then HostIP = SendingHostClassC End If
        If InStr(HostDNS,"Unknown ") > 0 Then
          HostDNS = HostDNS & " , claimed: " & ClaimedHostDNS
        End If
        CreateContact
          (objSpamContactsOU,HostIP,RealServerIP,HostDNS,MsgHeader,givenName,sn)
      End If
    End If
' Contact not found error
  Loop
End Sub

Creating contacts in the AD

The sub below creates the contacts in the AD that SMTPSPAMFilter.vbs checks.  Both the LoadHosts and ReadInItems Subs call this Sub to create the contacts in the AD and pass it the required and optional fields.  The description is based on how the host was determined to be a spamer.  Below is list of the possible descriptions:

Blacklisted: ?Blacklisted by [DNS name of blacklist server], claimed [host name from header]?
Dynamic: ?Dynamic Host: [Real DNS name acquired based on an NSLookup]?
Failed NSLookup: ?Failed NSLookup, claimed [host name from header]?
Unknown Host: ?Unknown Host: [RealDNS], claimed [host name from header]?
Bad Domain: ?Known Spamer: [RealDNS], claimed [host name from header]?
  - This example isn?t covered in this article but is supported by the attached script

As for the properties displayname should always be the real IP address of the sending server, givenName will be either ?Folder? or ?File? based on which input method was used to create the contact, sn will contain ?Blacklisted?, ?Dynamic?, ?Failed NSLookup?, "Unknown Host", or ?Known Spamer?.  Last info will contain the message header of the message that caused the contact to be created.  This field should contain all of the To, From, Time, Subject, and other header information which will be very useful when trying to see why a host was flagged as a spamer.

Sub CreateContact (objOU,cn,displayName,description,info,givenName,sn)
 
Dim objContact
 
Set objContact = objOU.Create("contact", "CN=" & cn)
  objContact.Put "description", description
  objContact.Put "displayname", displayName
  objContact.Put "givenName", givenName
  objContact.Put "sn", sn
  objContact.Put "info", info
  objContact.SetInfo
 
Set objContact = Nothing
End Sub

Supporting functions and subs

GetField

This function will return the data to the left of the delimiter it is passed, for this script Chr(9) is used.  It also returns the remaining data in the ExtraData variable.  This variable should be declared at the top of the script so it is available, global, to all Functions and Subs.

Function GetField (Data,Delimiter)
  If InStr(Data,Delimiter) = 0 Then
    GetField = Data
    ExtraData = ""
    Exit Function
  End If
  GetField = Left(Data,InStr(Data,Delimiter)-1)
  ExtraData = Right(Data,Len(Data)-InStr(Data,Delimiter)-Len(Delimiter)+1)
End Function

CheckDomainName

This is the function that carries out multiple checks, and calls several other functions, to see if the host IP is a valid host and sets the variables that are used to populate the contact in the AD.  It will return the class C address and non-dynamic part of the DNS name if a message is from a dynamic\dial-up host, like 123-56-67-8.dial-up.msn.net.  This way all mail from that dynamic class C address block will be blocked from sending future e-mails to your server.  It will also check to see if the domain name is in the valid domain list, which is set at the top of the script with the Const ValidDomains = statement.  If the host is valid and not-dynamic then CheckDomainName returns blank data and sets ServerIP to nothing.

The function first check to make sure it was passed valid data in RealDNS.  Then it gets the non-dynamic part of the DNS name by calling the NonDynamicPart function. Next, it then checks to see if the sending host is a dynamic host by comparing the real DNS name and non-dynamic part of the DNS name.  If the host is dynamic then ServerIP is set to the class C address, so all e-mail sent from this address block will be blocked, since the host is part of a dynamic pool.

Next we check to see if the last part of the DNS name is in the valid domain list.  If so we need to carry out several checks to determine if we should block traffic from it.  First we see if the "valid" host isn?t dynamic by comparing the real DNS name and the non-dynamic DNS name.  If they are found to be the same, like mail.msn.net, both CheckDomainName and ServerIP are sent to blank, which will prevent a contact from being created and mail from being blocked from this host.  If they aren?t the same it must mean that the real DNS name had part of the sending servers IP address in it, like 1-2-3-4.dial-up.msn.net.  If so we then check to see if there is something in front of the valid part of the DNS name, in the previous example ?dial-up? is in front of the valid domain ?msn.net.?  If there is something between the dynamic parts and known DNS part the script will assume that it should block all mail from the class C address of the sending IP.  This is because the I assumes that dial-up, DSL, cable users, etc will be connecting to a different IP subnet then that companies outgoing mail servers.  This should be a pretty valid assumption for the larger ISPs since they normally have a dedicated class C just for their non-dynamic host, like mail servers.

Note: If you have users that connect via SMTP\POP3 to your Exchange servers from the outside\internet they will be prevented from sending e-mail if they are dialing into an ISP that assigns IPs in the same range of a block class C address.  This is because messages sent by users via a SMTP client will show-up as coming from their dialed up IP address.  To prevent this from happening you might want to modify the section talked about above to not block any data from known hosts by adding ServerIP = ?? and changing CheckDomainName = from "Known Dynamic Host: [" & NonDynamicRealDNS & "]" to ??.

If the non-dynamic part of the DNS name is found to be the same as a valid domain name, like where the real DNS name is 123-56-67-8. msn.net, then CheckDomainName and ServerIP are sent to blank.  Since there is a good chance that blocking this class C address will block all e-mail from this domain.

Last if the DNS name was not in the valid domain list it is assumed the host is a spamer and the variables that are used to populate the contact are sent.

Function CheckDomainName (RealServerIP,ServerIP,RealDNS,CheckStatus)
 
If RealDNS = "" or RealDNS = "Invalid" Then Exit Function End If
 
' Will strip out the IP address from a host name
 
' Example: 1-2-3-4.dial-up.msn.net will become dial-up.msn.net
 
NonDynamicRealDNS = NonDynamicPart(RealDNS,RealServerIP)
 
If RealDNS <> NonDynamicRealDNS Then
   
' If message was sent from a dynamic host then block by the Class C address
   
CheckStatus = "Dynamic Host"
   
CheckDomainName = "Unknown Dynamic Host: [" & NonDynamicRealDNS & "]"
   
ServerIP = GetClassC(ServerIP)
 
End If
 
If InStr(LCase(ValidDomains),LCase(GetPartialDomain (RealDNS,2))) > 0 Then
   
If NonDynamicRealDNS = RealDNS Then ' Example mail.msn.com, site won't be blocked
     
CheckDomainName = ""
     
ServerIP = ""
   
Else ' Dynamic host like 1-2-3-4.msn.net or 1-2-3-4.dial-up.msn.net
    
 ' Makes sure the non-dynamic part isn't a valid domain name, like 1-2-3-4.msn.net
     
If InStr(LCase(ValidDomains),LCase(NonDynamicRealDNS)) = 0 Then
       
' If RealDNS = 1-2-3-4.dial-up.msn.net this would return dial-up.msn.net
       
CheckDomainName = "Known Dynamic Host: [" & NonDynamicRealDNS & "]"
       
CheckStatus = "Dynamic host"
     
Else
        ' Non-dynamic part is in the list of valid domains so don't filter it
        ' Example: 1-2-3-4.msn.net would equal msn.net
       
CheckDomainName = ""
       
ServerIP = ""
     
End If ' NonDynamicDNS in valid domain list
   
End If '  NonDynamicDNS is same as RealDNS
 
Else ' The host name wasn't in the valid domain name list and isn't blacklisted
   
' If CheckStatus hasn't been set yet then the host must be a non-dynamic unknown host
   
If CheckStatus = "" Then
     
CheckStatus = "Unknown Host"
     
CheckDomainName = "Unknown Host: [" & RealDNS & "]"
   
End If
 
End If ' RealDNS contains a valid domain
End Function

 

Building a List of Spam Hosts

Columnist's Index
Page 1 | Page 2 | Page 3 | Page 4 | Page 5

Disclaimer: Your use of the information contained in these pages is at your sole risk. All information on these pages is provided "as is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Stephen Bryant or Pro Exchange. OutlookExchange.Com, Stephen Bryant and Pro Exchange shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.

Copyright Stephen Bryant 2008