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.
%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
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
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
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
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
'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
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
'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
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
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
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