views:

532

answers:

2

I know how to look up a hostname from an IPv4 in VB using the GetHostByAddr Windows API call (this works great). However, that function does not allow one to specify the DNS server to use. Sometimes the default company DNS servers are fine, but other times I need to specify an external DNS server for lookups, and I don't think doing a shell nslookup and parsing the output is the best method, here.

Note: this is actually going to be used as VBA code in an Excel workbook to help someone else do his job, and it's not worth writing a big application when some simple functionality is all he needs.

I thought I had possibly found an answer in the API call getnameinfo but careful reading seems to indicate it does not offer a servername parameter.

After some intense searching, I found reference to the pExtra parameter to the DNSQuery function. But I don't even know how to begin to use that in VB6.

Could anyone help me out in any way with doing a DNS lookup from VB6, specifying the servername to use?

A full working solution would of course be nice, but I'm willing to work: just point me in the right direction.

UPDATE: For some odd reason it didn't click that DNSQuery was a Windows API call. It just didn't sound like one. I certainly would have been able to make more headway on the problem if I'd gathered that one tiny detail.

A: 

Can you use the DNS WMI provider to set the DNS of the system then use GetHostByAddr

Jeremy Petzold
I will check and see if the user is okay with this, but I suspect that he won't be (I wouldn't like it either) as if it would need to get changed back, and an error could occur which would leave it pointing to the wrong DNS server, which is not okay.
Emtucifor
this is true.Is this part of legacy code or do you have some latitude to take advantage of newer frameworks like .net?
Jeremy Petzold
It's just some automation to help the network security guy do his job. It took about 30 minutes to "spiff up" his Excel workbook for him. He also asked if I could give him a parameter to specify servername. Now I'm curious! If it comes to writing a whole application, then I can use C# to my heart's content. But Excel (2003) does 99% of what he wants, already.
Emtucifor
A: 

Try this:

Option Explicit

Private Declare Function DnsQuery Lib "dnsapi" Alias "DnsQuery_A" (ByVal strname As String, ByVal wType As Integer, ByVal fOptions As Long, ByVal pServers As Long, ppQueryResultsSet As Long, ByVal pReserved As Long) As Long
Private Declare Function DnsRecordListFree Lib "dnsapi" (ByVal pDnsRecord As Long, ByVal FreeType As Long) As Long
Private Declare Function lstrlen Lib "kernel32" (ByVal straddress As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, ByVal Source As Long, ByVal Length As Long)
Private Declare Function inet_ntoa Lib "ws2_32.dll" (ByVal pIP As Long) As Long
Private Declare Function inet_addr Lib "ws2_32.dll" (ByVal sAddr As String) As Long

Private Const DnsFreeRecordList         As Long = 1
Private Const DNS_TYPE_A                As Long = &H1
Private Const DNS_QUERY_BYPASS_CACHE    As Long = &H8

Private Type VBDnsRecord
    pNext           As Long
    pName           As Long
    wType           As Integer
    wDataLength     As Integer
    flags           As Long
    dwTel           As Long
    dwReserved      As Long
    prt             As Long
    others(35)      As Byte
End Type

Private Sub Command1_Click()
    MsgBox Resolve("google.com", "208.67.222.222")
End Sub

Private Function Resolve(sAddr As String, Optional sDnsServers As String) As String
    Dim pRecord     As Long
    Dim pNext       As Long
    Dim uRecord     As VBDnsRecord
    Dim lPtr        As Long
    Dim vSplit      As Variant
    Dim laServers() As Long
    Dim pServers    As Long
    Dim sName       As String

    If LenB(sDnsServers) <> 0 Then
        vSplit = Split(sDnsServers)
        ReDim laServers(0 To UBound(vSplit) + 1)
        laServers(0) = UBound(laServers)
        For lPtr = 0 To UBound(vSplit)
            laServers(lPtr + 1) = inet_addr(vSplit(lPtr))
        Next
        pServers = VarPtr(laServers(0))
    End If
    If DnsQuery(sAddr, DNS_TYPE_A, DNS_QUERY_BYPASS_CACHE, pServers, pRecord, 0) = 0 Then
        pNext = pRecord
        Do While pNext <> 0
            Call CopyMemory(uRecord, pNext, Len(uRecord))
            If uRecord.wType = DNS_TYPE_A Then
                lPtr = inet_ntoa(uRecord.prt)
                sName = String(lstrlen(lPtr), 0)
                Call CopyMemory(ByVal sName, lPtr, Len(sName))
                If LenB(Resolve) <> 0 Then
                    Resolve = Resolve & " "
                End If
                Resolve = Resolve & sName
            End If
            pNext = uRecord.pNext
        Loop
        Call DnsRecordListFree(pRecord, DnsFreeRecordList)
    End If
End Function
wqw
Hey, that's great stuff. Thanks. Did you have it in a library somewhere or did you build it custom for this answer?
Emtucifor
Had to custom clean up some samples from The Bathroom Wall of Code :-))
wqw
Will get back to you, don't fret...
Emtucifor
@wqw: I did come up with a somewhat polished working function. Since I did ask for a reverse lookup, I would have appreciated you mentioning that I needed to reverse the IP address and add .in-addr.arpa to the end in order to do reverse lookups. I also had to use DNS_TYPE_PTR, not A, which took quite a bit of fiddling to get working. Since you did make a material contribution to my eventual solution, I'm accepting your answer. Your code also needs Split(sDnsServers, ",") or some other delimiter specified. And I have to say that I'm NOT fond of the style of Hungarian notation you used.
Emtucifor
`Split` w/o delimiter just splits on space, so you could use "8.8.8.8 8.8.4.4" (google public DNS) for sDnsServers param. I'm kind of glad the hungarian is the most obvious thing that bothers you here. I've been long enough in the industry to see hungarian come, then being denounced, then probably come again as application hungarian (http://www.joelonsoftware.com/articles/Wrong.html). Don't fret over it, not using system hungarian is not better than using it. This is not something you can be right or wrong about and 99% of the VB6 code in the world is system hungarian decorated.
wqw
I didn't know that Split uses space as the default. Thanks. Application hungarian is what I use, I'm familiar with that article. And I'm doing my part to try to knock the worldwide VB6 system hungarian code down to 98%! System hungarian isn't morally or factually right or wrong, but practically wrong, yes! It just clutters everything up for no good reason. I understand style is subjective, but style matters nonetheless. A lot! It also affects the speed of comprehension.
Emtucifor
Did you notice I'm using Call on API functions only. That's style on the VB part. The hungarian notation -- you'll see the same volume of it when reading C/C++ WinAPI code. In fact not using it might lower someone's comprehension of this kind of API code, I think. Anyway, 98% is a good target :-)))
wqw