Monday, August 1, 2011

Excel User Defined Function - nslookup

Recently I have found myself in a position where I needed to run basic networking commands on numerous remote devices. At the end of the day I often found myself with several command prompts or cygwin bash shells open where I had to run umpteen nslookup commands. 

It didn't take long for me to find this whole process entirely unsatisfactory. I wanted to a way to run these commands very quickly and have the results returned in a place that I could refer to afterwards. I turned to the internet and ultimately did not find a simple, free, trustworthy utility that I could use to serve this purpose.

I turned to Excel and decided to write a user defined function that completes an nslookup command. The function has two parameters as described below:

nslookup( lookupVal, [addressOpt] )

lookupVal: represents the hostname or ip address that you wish to lookup

addressOpt: An integer that can have one of the values below

<blank> Auto Detect
0 Auto Detect
1 Forced Address Lookup
2 Forced Name Lookup

Here's some examples of how to run the code

The full Excel UDF is shown below. In order to get this function working properly you should save copy this code into a module, save the Excel File as an .xlam file, and add the file as an Add-in to Excel. A subsequent blog post will detail this process. Alternatively you can just add it as a function within an Excel file if you only need to use it in that file.

Public Function NSLookup(lookupVal As String, Optional addressOpt As Integer) As String
   Const NAME_LOOKUP = 2
   Const AUTO_DETECT = 0
   'Skip everything if the field is blank
   If lookupVal <> "" Then
        Dim oFSO As Object, oShell As Object, oTempFile As Object
        Dim sLine As String, sFilename As String
        Dim intFound As Integer
        Set oFSO = CreateObject("Scripting.FileSystemObject")
        Set oShell = CreateObject("Wscript.Shell")
        'Handle the addresOpt operand
        'Regular Expressions are used to complete a substring match for an IP Address
        'If an IP Address is found, a DNS Name Lookup will be forced
        If addressOpt = AUTO_DETECT Then
            ipLookup = FindIP(lookupVal)
            If ipLookup = "" Then
                addressOpt = ADDRESS_LOOKUP
                addressOpt = NAME_LOOKUP
                lookupVal = ipLookup
            End If
        'Do a regular expression substring match for an IP Address
        ElseIf addressOpt = NAME_LOOKUP Then
            lookupVal = FindIP(lookupVal)
        End If
        'Run the nslookup command
        sFilename = oFSO.GetTempName
        oShell.Run "cmd /c nslookup " & lookupVal & " > " & sFilename, 0, True
        Set oTempFile = oFSO.OpenTextFile(sFilename, 1)
        Do While oTempFile.AtEndOfStream <> True
            sLine = oTempFile.Readline
            cmdStr = cmdStr & Trim(sLine) & vbCrLf
        oFSO.DeleteFile (sFilename)
        'Process the result
        intFound = InStr(1, cmdStr, "Name:", vbTextCompare)
        If intFound = 0 Then
            NSLookup = "NotFound"
            Exit Function
        ElseIf intFound > 0 Then
            'TODO: Cleanup with RegEx
            If addressOpt = ADDRESS_LOOKUP Then
                loc1 = InStr(intFound, cmdStr, "Address:", vbTextCompare) + InStr(intFound, cmdStr, "Addresses:", vbTextCompare)
                loc2 = InStr(loc1, cmdStr, vbCrLf, vbTextCompare)
                nameStr = Trim(Mid(cmdStr, loc1 + 8, loc2 - loc1 - 8))
            ElseIf addressOpt = NAME_LOOKUP Then
                loc1 = InStr(intFound, cmdStr, "Name:", vbTextCompare)
                loc2 = InStr(loc1, cmdStr, vbCrLf, vbTextCompare)
                nameStr = Trim(Mid(cmdStr, loc1 + 5, loc2 - loc1 - 5))
            End If
        End If
        NSLookup = nameStr
        NSLookup = "N/A"
    End If
End Function

Function FindIP(strTest As String) As String
    Dim RegEx As Object
    Dim valid As Boolean
    Dim Matches As Object
    Dim i As Integer
    Set RegEx = CreateObject("VBScript.RegExp")
    RegEx.Pattern = "\b(?:\d{1,3}\.){3}\d{1,3}\b"
    valid = RegEx.test(strTest)
    If valid Then
        Set Matches = RegEx.Execute(strTest)
        FindIP = Matches(0)
        FindIP = ""
    End If
End Function