Using Microsoft DNS API to Handle in-Memory DNS Packets
Everyone knows what DNS is: basically it's the system to translate names into IP addresses.
On Windows, there're many ways to do that:
- gethostbyname — blocking, synchronous, only returns the first IP address, deprecated by getaddrinfo.
- WSAAsyncGetHostByName is asynchronous. However parallel resolution of several names is not supported. Besides, it requires the GUI, since the notifications are sent to a windows message queue.
- getaddrinfo — blocking, synchronous.
Besides, the 3 functions mentioned above only use the DNS servers from system’s settings, and unless the “DNS client” service is stopped, they use system DNS cache.
Windows 2000 introduced the DNS API.
DnsQuery function from that API allows usage of any DNS server you want, obtaining much more information like TTL and other DNS record types. It also has options to bypass the system DNS cache, or only use the cache thus not sending any requests over the wire.
However it’s still blocking and synchronous, while for one of my tests I wanted to be able to perform truly asynchronous DNS lookups.
There are many cross-platform DNS client libraries out there. However, I strongly prefer using Microsoft-supported code where appropriate.
Fortunately, the Microsoft DNS API also contains a set of functions for manipulating in-memory DNS packets. Those two functions are DnsWriteQuestionToBuffer to prepare the DNS request, and DnsExtractRecordsFromMessage to parse the response received from the DNS server. I’ve found no sample code, that’s why I’m writing this article. Hopefully, it’ll be useful to someone.
This article is the simple (but still realistic) usage example for the 2 DNS API functions, DnsWriteQuestionToBuffer and DnsExtractRecordsFromMessage. Developing the asynchronous DNS client is clearly outside of its scope. For simplicity, I used blocking sockets to send and receive the data. If your goal is e.g. stress-test your DNS server, then it’s perfectly OK to use the proposed approach while exploiting WSASendTo/WSARecvFrom transport API with an I/O completion port kernel object, to get the maximum performance you can.
Here’s the demo project for Visual Studio 2008. Please excuse my style: I'd never wrote a 5-pages long function for any production system.
In my experience, malfunctioning DNS very often becomes the point of failure for both home users, and large corporate networks. Here’re the possible symptoms I encountered.
- My internet is broken.
- My internet is broken however the Skype still works.
I'm not a big fan of Skype, however Skype is the only popular software that doesn't give a **** to whether DNS is functioning or not.
- My internet is very slow.
- Only half of the web sites open.
- Everything works with the only exception of the Nintendo Wii (Wii only uses the 1-st DNS server listed in the DHCP response, while Windows quickly reverts to another one if the first one doesn’t work).
- Many different Active Directory troubles are possible: users not able to login, AD domain controllers’ replication is broken, and many others. Microsoft states that 70% of all AD problems are related to DNS.