Type HostDataType
sName As String * 64
sIPAddr As String * 15
lTTL As Long
End Type
Type MXRecordType
uHostInfo As HostDataType
iMXPrec As Integer
End Type
Type NSRecordType
uHostInfo As HostDataType
bTried As Integer
End Type
Type OtherRecordType
uHostInfo As HostDataType
sExtra As String * 64
End Type
Type SearchType
sQHostID As String * 64 'Query Host ID
sDNSaddr As String * 15 'DNS IP Address
iRType As Integer 'RR Type
End Type
Global gUDPBuffer$ 'Buffer for the UDP control
Dim mauMXRecords() As MXRecordType
Dim mauNSRecords() As NSRecordType
Dim mauHostRecords() As HostDataType
Global Const DNS_ERR_OK = -1
Global Const DNS_ERR_NORECORDS = 0
Global Const DNS_ERR_TIMEOUT = 1
Global Const DNS_ERR_NONAMESERVERS = 2
Global Const DNS_ERR_OTHER = 3
Procedures
Function GetHostLabel (psDataGram$, piStartPnt%, psLabel$) As Integer
Dim sLabel$, iDone%, iLabelCnt%, iCurPos%, iLabelPtr%, iInPtr%
'psDataGram$ is the DataGram
'piStartPnt% is the Starting Point in the DataGram, is updated after processing
iCurPos% = piStartPnt%
'This is for our pointer in the datagram which can be moved due to compression...
iLabelCnt% = piStartPnt%
iDone% = False
While Not iDone%
'Get the count of our label
iLabelPtr% = Asc(Mid$(psDataGram$, iLabelCnt%, 1))
'Is it 0?
If iLabelPtr% = 0 Then
'Yes, if we arent in a pointed to label then increment our rRecord pointer
'to point to one past this label
If Not iInPtr% Then iCurPos% = iLabelCnt% + 1
'We're done...
iDone% = True
Else
'Is this a pointer?
If (iLabelPtr% And 192) = 192 Then
'Yes, so set our inpointer flag
If Not iInPtr% Then
iInPtr% = True
iCurPos% = iLabelCnt% + 2
End If
'If we have part of a label, append a dot
'Our pointer can only point to null terminated labels so
'our datagram pointer for the next field is 2 bytes past this pointer
'Now set our pointer to the place where the rest of the label is
iLabelCnt% = ((iLabelPtr% And 63) * 256) + Asc(Mid$(psDataGram$, iLabelCnt% + 1, 1)) + 1
Else
'Is this count below 64 characters
If iLabelPtr% < 65 Then
'If we already have a part of the label then add a dot
If sLabel$ <> "" Then sLabel$ = sLabel$ + "."
'add the characters into the field
sLabel$ = sLabel$ + Mid$(psDataGram$, iLabelCnt% + 1, iLabelPtr%)
'Increment our pointer by the number of characters we read...
iLabelCnt% = iLabelCnt% + iLabelPtr% + 1
Else
'An error because we have more characters than we are supposed to
'have for this label.. (64 characters)
GetHostLabel = DNS_ERR_OTHER
Exit Function
End If
End If
End If
Wend
piStartPnt% = iCurPos%
psLabel$ = sLabel$
GetHostLabel = True
End Function
Function GetHostsFromDNS (ctrlUDP As Control, ByVal piRecType%, ByVal psHostID$, pasDNSHost$(),
pauHostInfo() As OtherRecordType) As Integer
'Comments:
' This function retrieves the DNS records for the data I request based on the
' Type of records that I want...
Dim iNSCnt%, iMXCnt%, iCnt%, iHostCnt%, iRecCnt%, iSuc%
Dim uSearch As SearchType
On Local Error Resume Next
'Load up our DNS records...
iNSCnt% = UBound(pasDNSHost$)
If iNSCnt% = 0 Then
GetHostsFromDNS = DNS_ERR_NONAMESERVERS
Exit Function
End If
'Set up the name server array...
Erase mauNSRecords
ReDim mauNSRecords(iNSCnt%)
For iCnt% = 1 To iNSCnt%
mauNSRecords(iCnt%).uHostInfo.sIPAddr = pasDNSHost$(iCnt%)
Next
Erase mauMXRecords
Erase mauHostRecords
'Now let's do the lookup search, set our search options..
uSearch.sQHostID = LCase$(psHostID$)
uSearch.iRType = piRecType% And 255
iNSCnt% = 1
Do
'Place a DNS host in our search
uSearch.sDNSaddr = mauNSRecords(iNSCnt%).uHostInfo.sIPAddr
'Remark out this line in your application unless you wish
'to use this functionality.
PostStatusMessage "Searching from [" + RTrim$(mauNSRecords(iNSCnt%).uHostInfo.sIPAddr) + "]
for host: " + RTrim$(psHostID$)
'Get DNS Records
iSuc% = SearchDNS(ctrlUDP, uSearch)
If iSuc% = DNS_ERR_OK Then
Select Case piRecType%
Case 1, 12
iHostCnt% = UBound(mauHostRecords)
ReDim pauHostInfo(iHostCnt%)
If iHostCnt% > 0 Then
'Ok, now we need to return the host info back to the array...
For iCnt% = 1 To iHostCnt%
pauHostInfo(iCnt%).uHostInfo.sName = mauHostRecords(iCnt%).sName
pauHostInfo(iCnt%).uHostInfo.sIPAddr = mauHostRecords(iCnt%).sIPAddr
pauHostInfo(iCnt%).uHostInfo.lTTL = mauHostRecords(iCnt%).lTTL
Next
Exit Do
Else
Erase mauHostRecords
End If
Case 15
iMXCnt% = UBound(mauMXRecords)
If iMXCnt% > 0 Then
ReDim pauHostInfo(iMXCnt%)
For iCnt% = 1 To iMXCnt%
pauHostInfo(iCnt%).uHostInfo.sName = mauMXRecords(iCnt%).uHostInfo.sName
pauHostInfo(iCnt%).uHostInfo.sIPAddr = mauMXRecords(iCnt%).uHostInfo.sIPAddr
pauHostInfo(iCnt%).uHostInfo.lTTL = mauMXRecords(iCnt%).uHostInfo.lTTL
pauHostInfo(iCnt%).sExtra = Trim$(Str$(mauMXRecords(iCnt%).iMXPrec%))
Next
Exit Do
Else
Erase mauMXRecords
End If
End Select
Else
Select Case iSuc%
Case DNS_ERR_TIMEOUT
PostStatusMessage "Timeout waiting for packet response from [" + RTrim$
(mauNSRecords(iNSCnt%).uHostInfo.sIPAddr) + "]"
Case DNS_ERR_NORECORDS
PostStatusMessage "No Records from [" + RTrim$(mauNSRecords(iNSCnt%).uHostInfo.
sIPAddr) + "]"
Case DNS_ERR_OTHER
PostStatusMessage "Bad packet response from [" + RTrim$(mauNSRecords(iNSCnt%)
.uHostInfo.sIPAddr) + "]"
End Select
End If
iNSCnt% = iNSCnt% + 1
'If we don't have any left, then exit
If iNSCnt% > UBound(mauNSRecords) Then Exit Do
Loop
'Returns the count of the records in the array back to the user..
iRecCnt% = UBound(pauHostInfo)
If iRecCnt% > 0 Then GetHostsFromDNS = True
End Function
Function GetQRdata (psDataGram$, puQData As HostDataType, piDGPos%) As Integer
Dim icp%, iLabelCnt%, iDone%, sQName$
'First get the label information
icp% = piDGPos%
'We aren't done
iDone% = False
While Not iDone%
iLabelCnt% = Asc(Mid$(psDataGram$, icp%, 1))
If iLabelCnt% = 0 Then
icp% = icp% + 1
iDone% = True
Else
'Add a period to the end of the
If sQName$ <> "" Then sQName$ = sQName$ + "."
'add the characters into the field
sQName$ = sQName$ + Mid$(psDataGram$, icp% + 1, iLabelCnt%)
icp% = icp% + iLabelCnt% + 1
End If
Wend
'We can add four to the count since we have passes the QRData.
icp% = icp% + 4
piDGPos% = icp%
puQData.sName = sQName$
puQData.sIPAddr = " "
GetQRdata = True
End Function
Function GetRRData (psDataGram$, piDGPos%) As Integer
Dim sLabel$, iRType%, iGotOne%, iClass%, lTTL&, sRData$, sHostName$
Dim iCurPos%, iRDLen%, iCnt%, sPrec$, lTL1&, lTL2&, iGotIt%, sThisIP$
Dim iHostCnt%, iMXCnt%, iNSCnt%, iSuc%
On Local Error Resume Next
iHostCnt% = UBound(mauHostRecords)
iMXCnt% = UBound(mauMXRecords)
iNSCnt% = UBound(mauNSRecords)
'First get the label information - isn't important unless it is a Type A Record...
'in which we match it up with record information already in the system...
'We need this as a temporary pointer and we modify it at the end
iCurPos% = piDGPos%
'Now go after the label for this record
iSuc% = GetHostLabel(psDataGram$, iCurPos%, sLabel$)
If iSuc% <> DNS_ERR_OK Then
GetRRData = iSuc%
Exit Function
End If
'Now we need to check the type of the record
iRType% = (Asc(Mid$(psDataGram$, iCurPos%, 1)) * 256) + Asc(Mid$(psDataGram$, iCurPos% + 1, 1))
'Move us over two places
iCurPos% = iCurPos% + 2
'Get the class
iClass% = (Asc(Mid$(psDataGram$, iCurPos%, 1)) * 256) + Asc(Mid$(psDataGram$, iCurPos% + 1, 1))
'Move us over two more places
iCurPos% = iCurPos% + 2
'Get the Time to Live of the record
lTTL& = ((Asc(Mid$(psDataGram$, iCurPos%, 1)) * 256&) + Asc(Mid$(psDataGram$, iCurPos% + 1, 1))) * 65536
lTTL& = lTTL& + (Asc(Mid$(psDataGram$, iCurPos% + 2, 1)) * 256&) + Asc(Mid$(psDataGram$, iCurPos% + 3, 1))
'Now move over four places...
iCurPos% = iCurPos% + 4
' Grab the length of the RData field
iRDLen% = (Asc(Mid$(psDataGram$, iCurPos%, 1)) * 256&) + Asc(Mid$(psDataGram$, iCurPos% + 1, 1))
'Go past the RData length
iCurPos% = iCurPos% + 2
'Now get the RData itself for processing...
sRData$ = Mid$(psDataGram$, iCurPos%, iRDLen%)
'Now that we have the information set our pointer past the rData so that we can process this
Select Case iRType%
Case 1
'This is an A type record which means we have a host address for a record and
'we need to marry it up with the record that pertains to it..
'So first, we need to find the record
If Len(sRData$) = 4 Then
sThisIP$ = Trim$(Str$(Asc(Mid$(sRData$, 1, 1)))) + "."
sThisIP$ = sThisIP$ + Trim$(Str$(Asc(Mid$(sRData$, 2, 1)))) + "."
sThisIP$ = sThisIP$ + Trim$(Str$(Asc(Mid$(sRData$, 3, 1)))) + "."
sThisIP$ = sThisIP$ + Trim$(Str$(Asc(Mid$(sRData$, 4, 1))))
'Check Name Servers....
For iCnt% = 1 To iNSCnt%
If LCase$(RTrim$(mauNSRecords(iCnt%).uHostInfo.sName)) = LCase$(sLabel$) Then
If RTrim$(mauNSRecords(iCnt%).uHostInfo.sIPAddr) = "" Then
iGotOne% = iCnt%
Exit For
End If
End If
Next
If iGotOne% <> 0 Then
mauNSRecords(iGotOne%).uHostInfo.sIPAddr = sThisIP$
Else
'If we don't have a name server host, then check the MX records for
'this record.
For iCnt% = 1 To iMXCnt%
If LCase$(RTrim$(mauMXRecords(iCnt%).uHostInfo.sName)) = LCase$(sLabel$) Then
If RTrim$(mauMXRecords(iCnt%).uHostInfo.sIPAddr) = "" Then
iGotOne% = iCnt%
Exit For
End If
End If
Next
If iGotOne% <> 0 Then
'Since we got one, add the IP address to the record
mauMXRecords(iGotOne%).uHostInfo.sIPAddr = sThisIP$
Else
'If we don't have a host, then check the other records for
'this record.
For iCnt% = 1 To iHostCnt%
If LCase$(RTrim$(mauHostRecords(iCnt%).sName)) = LCase$(sLabel$) Then
If RTrim$(mauHostRecords(iCnt%).sIPAddr) = "" Then
iGotOne% = iCnt%
Exit For
End If
End If
Next
If iGotOne% <> 0 Then
'Since we got one, add the IP address to the record
mauHostRecords(iGotOne%).sIPAddr = sThisIP$
Else
'If we don't have a host for this IP address, then
'use the label sent with the record and use this
'IP address for the record and add to the host list
If iHostCnt% = 0 Then
iHostCnt% = iHostCnt% + 1
ReDim Preserve mauHostRecords(iHostCnt%)
mauHostRecords(iHostCnt%).sName = sLabel$
mauHostRecords(iHostCnt%).sIPAddr = sThisIP$
mauHostRecords(iHostCnt%).lTTL = lTTL&
End If
End If
End If
End If
iCurPos% = iCurPos% + iRDLen%
Else
GetRRData = DNS_ERR_OTHER
Exit Function
End If
Case 2
'This is a name server that we add to the list of information...
'The name server adds a name to the entry
iSuc% = GetHostLabel(psDataGram$, iCurPos%, sHostName$)
If iSuc% = DNS_ERR_OK Then
iGotIt% = False
For iCnt% = 1 To iNSCnt%
If LCase$(RTrim$(mauNSRecords(iCnt%).uHostInfo.sName)) = LCase$(sHostName$) Then
iGotIt% = True
Exit For
End If
Next
If Not iGotIt% Then
'We need to add one to the record type
iNSCnt% = iNSCnt% + 1
ReDim Preserve mauNSRecords(iNSCnt%)
mauNSRecords(iNSCnt%).uHostInfo.sName = sHostName$
mauNSRecords(iNSCnt%).uHostInfo.sIPAddr = " "
mauNSRecords(iNSCnt%).uHostInfo.lTTL = lTTL&
End If
Else
GetRRData = DNS_ERR_OTHER
Exit Function
End If
Case 5, 12
iSuc% = GetHostLabel(psDataGram$, iCurPos%, sHostName$)
If iSuc% = DNS_ERR_OK Then
iGotIt% = False
For iCnt% = 1 To iHostCnt%
If LCase$(RTrim$(mauHostRecords(iCnt%).sName)) = LCase$(sHostName$) Then
iGotIt% = True
Exit For
End If
Next
If Not iGotIt% Then
iHostCnt% = iHostCnt% + 1
ReDim Preserve mauHostRecords(iHostCnt%)
mauHostRecords(iHostCnt%).sName = sHostName$
mauHostRecords(iHostCnt%).sIPAddr = " "
mauHostRecords(iHostCnt%).lTTL = lTTL&
End If
Else
GetRRData = DNS_ERR_OTHER
Exit Function
End If
Case 15
'This is an MX record and so we need to indicate this...
sPrec$ = Mid$(psDataGram$, iCurPos%, 2)
iCurPos% = iCurPos% + 2
iSuc% = GetHostLabel(psDataGram$, iCurPos%, sHostName$)
If iSuc% = DNS_ERR_OK Then
'We need to add one to the record type
iMXCnt% = iMXCnt% + 1
ReDim Preserve mauMXRecords(iMXCnt%)
mauMXRecords(iMXCnt%).uHostInfo.sName = sHostName$
mauMXRecords(iMXCnt%).uHostInfo.sIPAddr = " "
mauMXRecords(iMXCnt%).uHostInfo.lTTL = lTTL&
mauMXRecords(iMXCnt%).iMXPrec = (Asc(Left$(sPrec$, 1)) * 256) + Asc(Right$(sPrec$, 1))
Else
GetRRData = DNS_ERR_OTHER
Exit Function
End If
End Select
piDGPos% = iCurPos%
GetRRData = True
End Function
Function ParseDataGram (psDataGram$, piRecType%) As Integer
'This procedure parses the datagram into its component parts
Dim iQdCount%, iAnCount%, iNsCount%, iArCount%
Dim iDone%, iCurPnt%, iLabelCnt%, sTemp$, iLabelpnt%
Dim iCnt%, iSuc%
Dim uQHostRecord() As HostDataType
iQdCount% = (Asc(Mid$(psDataGram$, 5, 1)) * 256) + Asc(Mid$(psDataGram$, 6, 1))
iAnCount% = (Asc(Mid$(psDataGram$, 7, 1)) * 256) + Asc(Mid$(psDataGram$, 8, 1))
iNsCount% = (Asc(Mid$(psDataGram$, 9, 1)) * 256) + Asc(Mid$(psDataGram$, 10, 1))
iArCount% = (Asc(Mid$(psDataGram$, 11, 1)) * 256) + Asc(Mid$(psDataGram$, 12, 1))
iCurPnt% = 13
If iQdCount% > 0 Then
'Let's get the question data from each record...
For iCnt% = 1 To iQdCount%
ReDim Preserve uQHostRecord(iCnt%)
If Not GetQRdata(psDataGram$, uQHostRecord(iCnt%), iCurPnt%) Then
ParseDataGram = DNS_ERR_OTHER
Exit Function
End If
Next
End If
'Now get the Answer Data...
If iAnCount% > 0 Then
For iCnt% = 1 To iAnCount%
If Not GetRRData(psDataGram$, iCurPnt%) Then
ParseDataGram = DNS_ERR_OTHER
Exit Function
End If
Next
End If
If iNsCount% > 0 Then
For iCnt% = 1 To iNsCount%
If Not GetRRData(psDataGram$, iCurPnt%) Then
ParseDataGram = DNS_ERR_OTHER
Exit Function
End If
Next
End If
If iArCount% > 0 Then
For iCnt% = 1 To iArCount%
If Not GetRRData(psDataGram$, iCurPnt%) Then
ParseDataGram = DNS_ERR_OTHER
Exit Function
End If
Next
End If
ParseDataGram = DNS_ERR_OK
End Function
Function SearchDNS (ctrlUDP As Control, search As SearchType) As Integer
Dim sLabels$(), iLblCnt%
Dim sdatagram$
Dim iDot%, done%, lTimeOut&, suc%, iCnt%
Dim xTries%, sMsgType$
Dim sID$, sHostID$
'Let's get the host ID information..
sHostID$ = RTrim$(search.sQHostID)
'Create Datagram ... does recursion by default...
sdatagram$ = Chr$(1) + String$(2, 0) + Chr$(1) + String$(6, 0)
'Ok, now parse the host information...
While InStr(sHostID$, ".") > 0
iDot% = InStr(sHostID$, ".")
iLblCnt% = iLblCnt% + 1
ReDim Preserve sLabels(iLblCnt%)
sLabels(iLblCnt%) = Left$(sHostID$, iDot% - 1)
sHostID$ = Mid$(sHostID$, iDot% + 1)
Wend
If Len(sHostID$) > 0 Then
iLblCnt% = iLblCnt% + 1
ReDim Preserve sLabels(iLblCnt%)
sLabels(iLblCnt%) = sHostID$
End If
'Now prep the host string for use in the search...
Select Case search.iRType
Case 12
If Not (iLblCnt% > 0 And iLblCnt% < 5) Then Exit Function
For iCnt% = iLblCnt% To 1 Step -1
sdatagram$ = sdatagram$ + Chr$(Len(sLabels(iCnt%))) + sLabels(iCnt%)
Next
sdatagram$ = sdatagram$ + Chr$(7) + "IN-ADDR" + Chr$(4) + "ARPA"
Case Else
For iCnt% = 1 To iLblCnt%
sdatagram$ = sdatagram$ + Chr$(Len(sLabels(iCnt%))) + sLabels(iCnt%)
Next
End Select
sdatagram$ = sdatagram$ + Chr$(0)
sdatagram$ = sdatagram$ + Chr$(0) + Chr$(search.iRType) + Chr$(0) + Chr$(1)
'Datagram is completed
sID$ = Chr$(Int(Rnd * 255)) + Chr$(Int(Rnd * 255))
Err = 0
ctrlUDP.RemoteHost = RTrim$(search.sDNSaddr)
ctrlUDP.RemotePort = 53
'ctrlUDP.LocalPort = 53
ctrlUDP.Active = True
ctrlUDP.DataToSend = sID$ + sdatagram$
If Err <> 0 Then Exit Function
lTimeOut& = glTicks& + 10
gUDPBuffer$ = " "
While Not Left$(gUDPBuffer$, 2) = sID$
If glTicks& > lTimeOut& Then
SearchDNS = DNS_ERR_TIMEOUT
Exit Function
End If
DoEvents
Wend
ctrlUDP.Active = False
If RTrim$(gUDPBuffer$) <> "" Then
'SaveDataGram
If ((Asc(Mid$(gUDPBuffer$, 3, 1)) And 128) <> 128) And ((Asc(Mid$(gUDPBuffer$, 4, 1))
And 15) <> 0) Then
SearchDNS = DNS_ERR_OTHER
Exit Function
End If
sdatagram$ = gUDPBuffer$
If ParseDataGram(sdatagram$, search.iRType) = DNS_ERR_OK Then
If search.iRType = 15 Then SortMXRecords
SearchDNS = True
End If
End If
End Function
Sub SortMXRecords ()
Dim TempRec As MXRecordType
Dim iRecCnt%, bFlip%, iCnt%
On Local Error Resume Next
'Get the array count
iRecCnt% = UBound(mauMXRecords)
'If we don't have at least two, then it does us no good
If iRecCnt% < 2 Then Exit Sub
'Set our flip to force the loop
bFlip% = True
While bFlip%
'Ok, now reset the flip for the comparison
bFlip% = False
'Loop through the records
For iCnt% = 1 To iRecCnt% - 1
'If this records MX Prec is greater than the next...
If mauMXRecords(iCnt%).iMXPrec > mauMXRecords(iCnt% + 1).iMXPrec Then
'Copy the next record's data into the temp record
TempRec = mauMXRecords(iCnt% + 1)
'Copy this record into the next record
mauMXRecords(iCnt% + 1) = mauMXRecords(iCnt%)
'Copy the temp record into this record
mauMXRecords(iCnt%) = TempRec
'Now Set our flip to true
bFlip% = True
End If
'Do it again
Next
Wend
'We're done!
End Sub
The UDPPORT.VBX is made by DevSoft, Inc. You can register the UDPPORT.VBX on CompuServe's
Shareware Registration forum, or by calling PSL. The nag screens that appear are because you
do not have a license file and I am not providing mine because I am not selling the product
VBCTL3D.VBX is made by Simplex Solutions. You can register this control on CompuServe's
Shareware Registration forum. The control provides 3D appearances to controls and forms.
Also included is a file called VBEXIT3D.EXE which is a file that does a clean removal of the
VBX in design mode. You do not need it for the finished EXE - it's just used in the design
mode.
Send e-mail to me at john-r@vbonline.com
Click here to go back to the November '95 Article Index