LotusScript agent to parse log.nsf on an SMTP server

This LotusScript agent reads log.nsf and creates a document for every unique IP address it finds that’s relaying off of the server.

Don’t put this in your log.nsf — I created a new copy of mine and parsed a month’s worth of data. Putting this in your production log.nsf is a no-no! :p

I’ll break this down and comment further as time permits.




smtplogscrape.vbs

%REM
   Agent scrapeMailHosts
   Created Feb 6, 2013 by Thom Rosario
   Description: parses log.nsf and creates one document for each unique relaying host
%END REM

Option Public
Option Declare
   Dim db As NotesDatabase
   Dim mailLogView, hostsView As NotesView
   Dim ip, hostname, ipArray() As String

Sub Initialize
   Dim session As New NotesSession
   Dim mailLogDoc As NotesDocument

   Set db = session.CurrentDatabase  
   Set mailLogView = db.GetView ("MailRoutingEvents")
   Set hostsView = db.GetView ("SMTPHosts")
   Set mailLogDoc = mailLogView.GetFirstDocument
   Call getExistingIPs (mailLogDoc)
   Call Main (mailLogDoc)
End Sub

%REM
   Sub setIPAndHostname
   Description: sets the global variables ip and hostname
   Method:
      Logs are formatted thusly:
      11/16/2012 10:52:19 PM  SMTP Server: 10.130.168.29 connected
      or if there's a hostname, then they're formatted like:
      11/16/2012 10:52:28 PM  SMTP Server: hostname.foo.com (10.130.150.15) connected  
%END REM

Sub setIPAndHostname (logEntry As Variant, doc As NotesDocument)
   Dim server, hasParen, ipTemp, hostTemp As Variant
   Dim serverFormula, parenFormula, ipFormula, hostFormula As String

   serverFormula = |@Left (@Right ("| + logEntry + |"; "SMTP Server: "); " connected")|
   server = Evaluate (serverFormula, doc)
   
   'Now test for (
   parenFormula = |@Contains ("|+ server(0) +|"; "(")|
   hasParen = Evaluate (parenFormula, doc)
   
   If hasParen(0) Then
      'Has paren, so set ip AND hostname
      'IP address is stored inside the parens
      ipFormula = |@Left(@Right ("|+server(0)+|"; "("); ")")|
      ipTemp = Evaluate (ipFormula, doc)
      ip = ipTemp(0)
      
      'hostname = @Left of the first space
      hostFormula = |@Left ("|+server(0)+|"; " ")|
      hostTemp = Evaluate (hostFormula, doc)
      hostname = hostTemp(0)
   Else
      'There was no paren, so the only value we got was the IP address
      ip = server(0)
      hostname = ""
   End if
End Sub

%REM
   Function isPopularRelayServer
   Description: determines whether or not the passed IP address 
   is one of the popular relay servers.  This is really quick and dirty,
   but it works faster than searching the view ever will 🙂
%END REM

Function isPopularRelayServer (ip As variant) As boolean
   If ip = "10.130.132.26" Or _
      ip = "10.130.150.12" Or _
      ip = "10.130.150.14" Or _
      ip = "10.130.164.40" Or _ 
      ip = "10.130.168.29" Then
   isPopularRelayServer = True
   Else  
     isPopularRelayServer = False
   End If
End Function

%REM
   Sub addNewIP
   Description: add the newly found IP address to the global 
   array of known IP addresses
%END REM

Sub addNewIP (ip As Variant)
  Dim newUbound As Integer
  
  newUbound = UBound (ipArray) + 1
  ReDim Preserve ipArray (newUbound)
  ipArray (newUbound) = ip
End Sub

%REM
   Sub getExistingIPs
   Description: pre-populates the view of IPs already scraped
%END REM

Sub getExistingIPs (doc As NotesDocument)
   Dim ipList As Variant
   Dim dbCol As String
   Dim count As Integer
   count = 0
  
   dbCol = |@DbColumn (notes:nocache; "":""; "SMTPHosts"; 1)|
   ipList = Evaluate (dbCol, doc)
   ForAll server In ipList
      ReDim Preserve ipArray(count)
      ipArray(count) = server
      count = count + 1
   End ForAll
End Sub

%REM
   Sub markLogDocProcessed
   Description: mark the current log document as processed
   so that it disappears out of the view
%END REM

Sub markLogDocProcessed (doc As NotesDocument)
   'Set the person who made the edits SDTech
   'Save the document
   doc.SDTech = "Thom Rosario"
   Call doc.Save (True, True)
End Sub

%REM
   Sub CreateHostDoc
   Description: creates a new document given the IP and hostname
%END REM
Sub createHostDoc (ip As variant, hostname As Variant)
   Dim hostDoc As NotesDocument
   Set hostDoc = db.CreateDocument
   hostDoc.Form = "SMTP Host"
   hostDoc.IPAddress = ip
   hostDoc.Host = hostname
   Call hostDoc.Save (True, True)
End Sub

%REM
   Function isRelayLogEntry
   Description: this function takes a line of text and determines
   whether or not it's a log entry showing that a server is relaying 
   off of this server
%END REM

Function isRelayLogEntry (logEntry As Variant, doc As NotesDocument) As Boolean
   'If the line has "SMTP Server:" and "connected", then we're interested 
   Dim hasServer, hasConnected As Variant
   Dim serverString, connectString, tempString As String

  'Get rid of single quotes in the string; they mess up rest of this function
  tempString = findReplace (logEntry, "'", "")
  
   'See if it's an SMTP server line
   serverString = |@Contains('| + tempString + |'; 'SMTP Server:')|
   hasServer = Evaluate (serverString, doc)

   If hasServer(0) Then
      'Now see if it's a server connecting
      connectString = |@Contains('| + tempString + |'; ' connected')|
      hasConnected = Evaluate (connectString, doc)
    
      'if has server and has connected then true
      If hasConnected(0) Then
         isRelayLogEntry = true
      Else
         isRelayLogEntry = false
      End If
   End If
End Function

%REM
  Sub Main
  Description: main logic loop
%END REM

Sub Main (mailLogDoc As NotesDocument)
   Dim docToProcess As NotesDocument
   Dim logBody As Variant  
  
   While Not(mailLogDoc Is Nothing)
      If Not mailLogDoc.HasItem ("SDTech") Then   
         If mailLogDoc.HasItem("EventList") Then
            logBody = mailLogDoc.EventList
            ForAll logLine In logBody
            If isRelayLogEntry (logLine, mailLogDoc) Then
               Call setIPAndHostname (logLine, mailLogDoc)
               If Not isPopularRelayServer (ip) Then
                  If Not isAlreadyRecorded (ip) Then
                     Call addNewIP (ip)
                     Call createHostDoc (ip, hostname)
                  End If 'Already Recorded                      
               End If 'Is Popular
            End If 'Is a relay log entry
         End ForAll 'Done parsing all lines
      End If 'Has the EventList field
      Set docToProcess = mailLogDoc
   End If 'No SDTech field
   Set mailLogDoc = mailLogView.GetNextDocument(mailLogDoc)
   Call markLogDocProcessed (docToProcess)
   Wend 'Done w/ current doc
End Sub


%REM
  Function findReplace
  Description: takes a source string, a character to find,
  and a character to replace it with
%END REM

Function findReplace (ByVal wholeString As Variant, find As String, ireplace As String) As String
   Dim checkString, saveLeft, leftString, rightString As String
   Dim n As Integer
  
   checkString = wholeString
   saveLeft = ""
   While InStr (1, checkString, find) <> 0
      n = InStr (1, checkString, find)
      leftString = Left (checkString, n-1)
      rightString = Right (checkString, Len (checkString) - n - Len (find) + 1)
      saveLeft = saveLeft + leftString + ireplace
      checkString = rightString
   Wend
   findReplace = saveLeft + checkString
End Function

%REM
   Function isAlreadyRecorded
   Description: compares the passed IP address to the SMTP Hosts
   view's IP addresses and returns true if found.
%END REM

Function isAlreadyRecorded (ip As Variant) As boolean
   Dim found As Boolean
   Dim i As Integer
   found = false

   For i = 0 To UBound(ipArray)
      If ip = ipArray(i) Then
      found = true
   End If
   Next
   isAlreadyRecorded = found
End Function


Leave a Reply

Your email address will not be published. Required fields are marked *