Advertisment

Build your own DNS 

author-image
PCQ Bureau
New Update

This is the concluding part of our series on  implementing our own DNS server. We said we could use our DNS server for purposes other than just a record lookup. One of the reasons we gave was for remote

authentication and a spin off of this is looking up records for spam control. The demonstration project given on this month's

PCQ Essential CD contains a client form that shows you what is happening. 

Advertisment

We have given the entire project in a zip file. Simply extract, maintaining the directory structure and open the Solution file in VS.NET 2003. When you run the project through the IDE, you will also see various debug lines in the output window that tells you from where the request was satisfied. The project uses an MS Access database (called 'ns.mdb'), which is saved in the application's startup folder. At present there is no script within the code to create this database, but we have provided it in the zip file. When run from VS.NET, this database needs to be in the BIN folder.

The project components 



The project contains four classes and a module, accompanied by two WinForms. The module contains various library functions we need in the other classes, such as converting octal numbers to strings and so on. The process of converting between the TCP packets and programmable data involves a heavy amount of octal to decimal conversions and bit-shifting. To understand this code -mostly within CDNSPacket.vb and NSCommon.vb-you would need a good C or Assembly background.

Direct

Hit!

Applies to: Advanced .NET programmers

USP: Using a DNS server for remote validation, useful for spam control 

Primary Link:

http://www.dnsstuff.com 

Google

keywords:
dns sockets vb.net
On PCQ Essential CD:

Developers_Lab\NameServer.zip
Advertisment

The two WinForms handle the UI. The frmStatus is our main form that shows a running display of incoming requests and replies. frmConfig allows you to set up the time to wait for when we forward requests to other DNS servers and the DNS server to forward to. You can set one forwarder here and a timeout value in milliseconds for less than an hour.



CCacheEntry is the simplest class we have and serves as a component class for CCache, which is a collection of CCacheEntry items. CCache is our in-memory runtime cache. This helps us perform faster lookups. For longer retention, we have the DBLookup class, which persists the data to our Access database file and also loads it into CCache.


Then we have the most complicated class-the CDNSPacket. This class deals with converting between the packets we send over TCP or UDP and what we can use in our program. The text-diagrams given in-line (like between lines 54 and 74) depict the packet as we get it for that information. The structure declaration below that is what we translate the packet to. ParsePacket and BuildPacket are our two workhorses here, converting between the two formats and the code is far from pretty.

How it works



Although the given project can easily be a Windows service, this is a Win32 application, with a form on screen. If you look at the form in VS.NET's designer, you will soon find out the secret that instead of going through complicated API, we have elected to implement the networking aspect using the Winsock control. There are two of them — wsDNSserver and wsDNSclient. This also explains the 'VB6 Compatibility' block in the first part of that form's code. The 



easiest way to write to get the code stage in frmStatus is to implement that form in VB6 and then use VS.NET 2003's, Upgrade
Wizard, launched on opening the older form, to automatically convert it. You will get a few warnings that you may ignore.

At startup, the DBLookup class is initialized, and all persisted records are loaded in a different cache. When a request comes in, it is picked up by the wsDNSserver control and the packet is parsed using CDNSPacket. Then, we check if we know the answer first in the runtime cache, then in the database cache. If it is found, a

suitable reply is sent back. If the answer existed in the database but not in runtime cache, it is copied there for next time. wsDNSclient listens for replies from wsDNSserver and updates the on-screen

information as well as the caches from time to time, allowing for a completely event-driven and apartment-threaded model. The best encapsulation of this is in the Lookup function

(frmStatus, 393-429), which goes like this.

Advertisment

Private Sub Lookup(ByVal ItemName As String, ByVal server As String)



...


dbResponse = RRDB.GetRecords(ItemName, 1)


For I = 0 To dbResponse.Length - 1


If (dbResponse(I).TTL > -1) Then


AddCacheEntry( _


dbResponse(I).RName, _


dbResponse(I).RData, _


dbResponse(I).TTL)






SendReply(dbResponse(I).RName)



...


End If


Next





' Create a request packet.


...


packet = Request.BuildPacket





' Send the request.


...


wsDNSclient.SendData(packet)


End Sub










The check 'ReplyHost.IsWaiting' is a test of the internal flag, where we resolve recursion problems. That is, since our form is both a server and a client, we need to know at each stage of a request and reply, how deep we are in calling ourselves. We need to prevent local replies if we are actually sending it out to another server or have been given other DNS servers to lookup (DNS Redirect). The 'IsWaiting' flag is true only for the main level in the recursion tree. The two main areas where this affects our program functionality is in Lookup and ParseReply. Lookup handles incoming requests at wsDNSserver and ParseReply handles incoming replies at wsDNSclient. You would

notice that SendReply (frmStatus, 475-510) has no such problem-it is called within Lookup or ParseReply at the right stage.

Code limitation



As of now, the code has one small limitation. It will return only 'A' records to the remote client, although it can itself receive any type of record. This facility exists in CDNSPacket, but it has not been hooked up completely in frmStatus. But this would be easy to do, since all you have to do is change the 'Qtype' and 'Rtype' values passed into SendReply and AddCacheEntry function calls wherever they

appear. At present time, this code stands untested as a suitable forwarder for other DNS servers, and cannot guarantee a comprehensible reply to all queries-'A' queries will still be handled

properly, according to RFC conventions.

Sujay V Sarma

Advertisment