Code archives/Networking/Query an DNS Server

This code has been declared by its author to be Public Domain code.

Download source code

Query an DNS Server by Klaas2003
This are some main function to directly query an Name server

Its written in BlitzPlus ... bt can be used with any Blitz version (just remove the GUI-Functions)

currently supported is the question for
A - the ip of a domain
MX - the mailhost for a domain

PTR is still not working ... will add this soon
maybe someon else got more luck then me !
If you solve the PTR Problem or got any questions write to:
rayzor-s-edge@...
;To Do's
;
;buffer overrun while pointer is leaving the bank
;
;this code is far fom completed but its works real well
;still have problems with PTR
;if you have questions please write to rayzor-s-edge@gmx.net

Global DNS_queryid	;an unique id for dns querys (will be incred by each request)
Global DNS_stream	;the main DNS Stream (UDP port 53)
Global pointer	;the main pointer for parsing the streams

Dim splitresult$(100)

Type query
	Field id
	
	Field nserver
	Field qr
	Field opcode
	Field aa
	Field tc
	Field rd
	Field ra
	Field z
	Field rcode

	Field stream

	Field qname$
	Field qtype
	Field qclass
	Field qsize
End Type

;create a window with a text area to show the results
hwnd=CreateWindow("text",0,0,500,600)
Global tarea=CreateTextArea(0,0,ClientWidth(hwnd),ClientHeight(hwnd),hwnd)
font = LoadFont("courier",14)
SetTextAreaFont(tarea,font)
SetGadgetLayout(tarea,1,1,1,1)

DNS_init()	;init the stream
DNS_Query(12345,parseip("212.79.208.3"),"www.google.de","A")	;ask your question

timer = CreateTimer(50)
Repeat
	WaitTimer(timer)
	Select WaitEvent()
	Case $803
		End
	End Select
	bank = DNS_waitQuery()
	If bank Then DNS_readQuery(bank)
Forever

Function DNS_init()
	;init first to get the udp stream
	DNS_Stream = CreateUDPStream(53)
End Function

Function DNS_waitQuery()
	;this function waits for an answer with the given id
	;or the timeout
	;this function parses all incoming querys but if the
	;query is not the coresponding one it reactivates itself with
	;a lowered timeout value
	If Not RecvUDPMsg(DNS_stream) Then Return False
	avail = ReadAvail(DNS_stream)
	If Not avail Return False
	Print "receiving a query "+avail+" bytes long"
	;read the incomming stream
	bank = CreateBank(avail)
	ReadBytes(bank,DNS_stream,0,avail)

	Return bank
End Function

Function DNS_Query(id,nserver,url$,typ$,class=1,rec=1)
	;this function builds the query structure and
	;feeds this structure with paramters
	q.query = New query
	
	queryid = id
	q\id = queryid
	q\qname = url
	q\qclass = class
	q\nserver = nserver
	
	If rec Then q\rd = 1
	
	Select Upper(typ)
	Case "A"
		q\qtype = 1
	Case "PTR"
		q\qtype = 12
	Case "MX"
		q\qtype = 15
	End Select
	
	;here the query structure is converted to an sendable stream
	DNS_createquerystream(q)
	;now send it and flush the structure
	If Not DNS_sendquery(q)
		Delete q
		Return False
	Else
		Delete q
		Return True
	EndIf
End Function

Function DNS_createquerystream(q.query)
	;this function converts the query structure to an real query streambank
	q\stream = CreateBank(512)
	
	;will not be reversed to little endian for fast lookup
	PokeShort(q\stream,0,q\id)

	b = q\rd Shl 8
	
	lePokeShort(q\stream,2,b)

	lePokeShort(q\stream,4,1)
	lePokeShort(q\stream,6,0)
	lePokeShort(q\stream,8,0)
	lePokeShort(q\stream,10,0)
	
	off = 12
	
	Length = Len(q\qname)
	
	nr = split(".",q\qname$)
	For i=0 To nr-1
		n$ = splitresult(i)
		l = Len(n)
		PokeByte(q\stream,off,l)
		off = off + 1
		For i2=1 To l
			c$ = Mid(n,i2,1)
			PokeByte(q\stream,off,Asc(c$))
			off=off+1
		Next
	Next
	PokeByte(q\stream,off,0)
	off=off+1
	lePokeShort(q\stream,off,q\qType)
	off=off+2
	lePokeShort(q\stream,off,q\qclass)
	off=off+2

	q\qsize = off
End Function

Function DNS_sendquery(q.query)
	;this function sends the query streambank to the name server specified in tzhe query structure
	If Not DNS_stream Then Return False
	WriteBytes(q\stream,DNS_Stream,0,q\qsize)
	
	SendUDPMsg(DNS_Stream,q\nserver,53)
	Print "Query send to "+DottedIP(q\nserver)

	Return True
End Function

Function DNS_ReadQuery(bank)
	;this is a lot of parsing
	
	If Not bank Then RuntimeError("invalid bank")
	Print "-- HEADER --"
	pointer = 12
	id = PeekShort(bank,0)
	Print "ID: "+id
	flag = lePeekShort(bank,2)
	Print "Flag: "+Right(Bin(flag),16)

	qr = (flag Shr 15) And 1
	opcode = (flag Shr 11) And 7
	aa = (flag Shr 10) And 1
	tc = (flag Shr 9) And 1
	rd = (flag Shr 8) And 1
	ra = (flag Shr 7) And 1
	z = (flag Shr 4) And 7
	rcode = (flag Shr 0) And 11

	Print "HEADER IN DETAILS"
	Print ""
	Write "Msg Type: "
	If qr
		Print "Answer"
	Else
		Print "Question"
	EndIf

	Write "OpCode: "
	Select opcode
	Case 0
		Print "standart"
	Case 1
		Print "inverse query"
	Case 2
		Print "a server status request"
	Case 3
		Print "reserved for future use"
	End Select

	Write "Authoritive: "
	If aa
		Print "Yes"
	Else
		Print "No"
	EndIf
	
	Write "Truncate: "
	If tc
		Print "Yes"
	Else
		Print "No"
	EndIf
	
	Write "Recursive desired: "
	If rd
		Print "Yes"
	Else
		Print "No"
	EndIf
	
	Write "Recursive available: "
	If ra
		Print "Yes"
	Else
		Print "No"
	EndIf

	Print "ZCode: "+z
		
	Write "RCode: "
	Select opcode
	Case 0
		Print "No error condition"
	Case 1
		Print "Format Error"
	Case 2
		Print "Server failure"
	Case 3
		Print "Name Error"
	Case 4
		Print "Not Implemented"
	Case 5
		Print "Refused"
	Case 6
		Print "Unkown"
	End Select

	qd = lePeekShort(bank,4)
	an = lePeekShort(bank,6)
	ns = lePeekShort(bank,8)
	ar = lePeekShort(bank,10)
	
	Print ""
	Print "DATA COUNT"
	Print ""
	Print "Questions : "+qd
	Print "Answers   : "+an
	Print "Athority  : "+ns
	Print "Additional: "+ar
	
	Print ""
	Print "-- DATA --"
	For i=1 To qd
		Print""
		Print "READING QUESTION "+i+"("+pointer+")"
		DNS_readqd(bank)
	Next
	For i=1 To an
		Print""
		Print "READING ANSWER "+i+"("+pointer+")"
		DNS_readresource(bank)
	Next
	For i=1 To ns
		Print""
		Print "READING AUTHORITY "+i+"("+pointer+")"
		DNS_readresource(bank)
	Next
	For i=1 To ar
		Print""
		Print "READING ADDITIONAL "+i+"("+pointer+")"
		DNS_readresource(bank)
	Next
End Function

Function DNS_readresource(bank)
	;this function parses the resource messages
	;resource messages are all appending message of a query
	;except the question messages
	
	name$ = DNS_decodeSubString(bank,pointer)
	
	Local b[10]
	For i=1 To 10
		b[i]=PeekByte(bank,pointer)
		pointer = pointer + 1
	Next
	
	ctype = (b[1] Shl 8) + b[2]
	class = (b[3] Shl 8) + b[4]
	ttl = (b[5] Shl 24) + (b[6] Shl 16) + (b[7] Shl 8) + b[8]
	rdlength = (b[9] Shl 8) + b[10]
	
	Print "NAME     :"+name$
	Print "TYPE     :"+ctype
	Print "CLASS    :"+class
	Print "TTL      :"+ttl+" sec"
	Print "RD Length:"+rdlength

	repointer = pointer

	Print ""
	Select ctype
	Case 1
		Print "Data is a host address"
		ip$ = decodeIPadress(bank,pointer)
		Print "RDATA    :"+ip
	Case 2
	Print "Data is an authoritative name server "
		txt$ = DNS_decodeSubString(bank,pointer)
		Print "RDATA    :"+txt
	Case 6
		Print "Data is a zone of authority"
		txt = DNS_decodeSubString(bank,pointer)
		Print "MNAME    :"+txt
		txt = DNS_decodeSubString(bank,pointer)
		Print "RNAME    :"+txt
		r1 = lePeekShort(bank,pointer)
		pointer=pointer+2
		r2 = lePeekShort(bank,pointer)
		pointer=pointer+2
		Print "SERIAL   :"+(r1 Shl 16)+r2

		r1 = lePeekShort(bank,pointer)
		pointer=pointer+2
		r2 = lePeekShort(bank,pointer)
		pointer=pointer+2
		Print "REFRESH  :"+(r1 Shl 16)+r2
		 
		r1 = lePeekShort(bank,pointer)
		pointer=pointer+2
		r2 = lePeekShort(bank,pointer)
		pointer=pointer+2
		Print "RETRY    :"+(r1 Shl 16)+r2

		r1 = lePeekShort(bank,pointer)
		pointer=pointer+2
		r2 = lePeekShort(bank,pointer)
		pointer=pointer+2
		Print "EXPIRE   :"+(r1 Shl 16)+r2

		r1 = lePeekShort(bank,pointer)
		pointer=pointer+2
		r2 = lePeekShort(bank,pointer)
		pointer=pointer+2
		Print "MINIMUM  :"+(r1 Shl 16)+r2
	Case 12
		Print "Data is a domain name pointer"
		txt = DNS_decodeSubString(bank,pointer)
		Print "RDATA    :"+txt
	Case 15
		Print "Data is a mail exchange"
		pre = lePeekShort(bank,pointer)
		pointer = pointer + 2
		Print "RDATA    :"+pre
		domain$ = DNS_decodeSubString(bank,pointer)
		Print "RDATA    :"+domain
	Default
		Print "Data is a unknown type"
	End Select
	pointer = repointer + rdlength
End Function

Function DNS_decodeSubString$(bank,off)
	;this is the main subroutine for parsing
	;it supports the query compression sheme
	;for DNS querys
	
	l = True
	While l
		l = PeekByte(bank,off)
		off = off + 1
		
		If l=0
			pointer = off
			Return Left(txt$,Len(txt)-1)
		ElseIf (l And 192)=192
			b1 = l
			b2 = PeekByte(bank,off)
			off = off + 1
			
			
			off2 = (b1 Shl 8) + b2
			off2 = (off2 And 16383)
;			Print "(jumping to "+off2+")"
			txt = txt + DNS_decodeSubString(bank,off2)
			pointer = off
			Return txt$
		Else
			For i = 1 To l
				b = PeekByte(bank,off)
				off = off + 1
				txt$ = txt$ + Chr(b)
			Next
			txt$ = txt$ + "."
		EndIf
	Wend
End Function

Function DNS_Readqd(bank)
	;this function parses question messages
	
	txt$ = DNS_decodeSubString(bank,pointer)
	
	Local b[4]
	For i=1 To 4
		b[i]=PeekByte(bank,pointer)
		pointer = pointer + 1
	Next

	Print "QNAME :"+txt
	Print "QTYPE :"+(b[1] Shl 8) + b[2]
	Print "QCLASS:"+(b[3] Shl 8) + b[4]
End Function

Function decodeIPadress$(bank,off)
	For i = 1 To 4
		txt$ = txt + dot$ + PeekByte(bank,off)
		dot$ = "."
		off = off + 1
	Next
	Return txt
End Function

;------------------------------------------------------------- HELP FUNCTIONS

Function split(seperator$,txt$)
	;splits an string with the given seperator and returns the nr of pieces
	;the pieces can be found in the "splitresult" Array
	pos=Instr(txt$,seperator$,1)	
	While (pos)
		splitresult(count)=Left(txt$,pos-Len(seperator))
		
		txt$=Right(txt$,Len(txt$)-pos-Len(seperator)+1)
		pos=Instr(txt$,seperator$,1)
		count=count+1
	Wend
	splitresult(count)=txt$
	
	count=count+1
	Return count
End Function

Function parseip(txt$)
	;converts an dotted ip like "192.0.0.1" to an integervalue
	nr = split(".",txt)
	If nr = 4
		b1 = Int(splitresult(0)) Shl 24
		b2 = Int(splitresult(1)) Shl 16
		b3 = Int(splitresult(2)) Shl 8
		b4 = Int(splitresult(3)) Shl 0
	EndIf
	
	Return b1+b2+b3+b4
End Function

Function lePokeShort(bank,offset,short)
	;because networkbytes are stored differently to x86 PC's (in network its little-endian)
	;this function swaps byte 1 and byte 2 in an short and inserts it in the given bank
	b1 = short And $ff
	b2 = (short Shr 8) And $ff
	PokeByte(bank,offset,b2)
	PokeByte(bank,offset+1,b1)
End Function

Function lePeekShort(bank,offset)
	;because networkbytes are stored differently to x86 PC's (in network its little-endian)
	;this function read a short from a bank and swaps byte 1 and byte 2 then returns the new value

	short = PeekShort(bank,offset)
	b2 = (short And $ff) Shl 8
	b1 = (short And $ff00) Shr 8
	Return (b2 + b1)
End Function

Function Print(txt$)
	AddTextAreaText(tarea,txt+Chr(10))
End Function

Function Write(txt$)
	AddTextAreaText(tarea,txt)
End Function

Comments

None.

Code Archives Forum