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.


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

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

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

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)
      'There was no paren, so the only value we got was the IP address
      ip = server(0)
      hostname = ""
   End if
End Sub

   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 🙂

Function isPopularRelayServer (ip As variant) As boolean
   If ip = "" Or _
      ip = "" Or _
      ip = "" Or _
      ip = "" Or _ 
      ip = "" Then
   isPopularRelayServer = True
     isPopularRelayServer = False
   End If
End Function

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

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

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

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

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

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

   Sub CreateHostDoc
   Description: creates a new document given the IP and hostname
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

   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

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
         isRelayLogEntry = false
      End If
   End If
End Function

  Sub Main
  Description: main logic loop

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

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

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
   findReplace = saveLeft + checkString
End Function

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

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
   isAlreadyRecorded = found
End Function

Leave a Reply

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