DNSQuery() Sample To Loop Through Multiple IP Addresses

Jeff from the Windows SDK team here.

I had a customer that wanted to just find the IP address that is registered in DNS, i.e., they wanted to get back the single public IP
address and not any of the other multihomed IP addresses.

Most APIs use the DNS Resolver Cache and return all of the all of the local addresses when you query the local machine name.

DNSQuery() has dozens of flags including DNS_QUERY_WIRE_ONLY, DNS_QUERY_BYPASS_CACHE,
DNS_QUERY_NO_HOSTS_FILE that looked promising. Now I just needed a way to test
it; I couldn’t find a sample that actually looped through the results. There is an old KB article with
sample code that calls DNSQuery() but it doesn’t account for multiple returned addresses.

So I thought I would write a blog with a sample of DNSQuery(); this is that blog.

#pragma comment(lib,"Iphlpapi.lib")
#pragma comment(lib,"dnsapi.lib")
#pragma comment(lib,"ws2_32.lib") 

#include "windows.h"
#include "stdio.h"
#include "windns.h" 

int wmain(int argc, WCHAR** argv)
{
       DNS_STATUS dnsStatus;
       IN_ADDR ipaddr;
       PDNS_RECORD ppQueryResultsSet, p;
       PIP4_ARRAY pSrvList = NULL;
       char temp[MAX_PATH];
       int iRecord = 0; 

       if (argc == 1 || argc > 3)
       {
              wprintf(L"Usage: %s hostname [IP addr of DNS server] \n\n", argv[0]);
              return -2;
       } 

       wprintf(L"Querying for host: %s\n", argv[1]); 

       if (argc == 3) // Get the IP address of the DNS server to query
       {
              pSrvList = (PIP4_ARRAY)LocalAlloc(LPTR, sizeof(IP4_ARRAY));
              if (!pSrvList)
              {
                     wprintf(L"PIP4_ARRAY allocation failed \n");
                     return -3;
              }                    

              sprintf_s(temp,MAX_PATH,"%S", argv[2]);  // Convert to ASCII
              pSrvList->AddrArray[0] = inet_addr(temp); //DNS (ASCII) to  IP address
              pSrvList->AddrCount = 1; 

              wprintf(L"Querying DNS Server: %s\n", argv[2]);
       }     

        dnsStatus = DnsQuery(argv[1], 
                           DNS_TYPE_A,
                           DNS_QUERY_WIRE_ONLY  
                          pSrvList, // Documented as reserved, but can take a PIP4_ARRAY for the DNS server
                           &ppQueryResultsSet,
                           NULL ); // Reserved

       if (dnsStatus)
       {

              wprintf(L"\nDNSQuery failed and returned %d, GLE = %d\n\n", dnsStatus, GetLastError()); 
              return -1;
       } 

       p = ppQueryResultsSet; 

       while (p) // Loop through the returned addresses
       {            

             iRecord++;
              wprintf(L"\nRecord #%d\n", iRecord);

               ipaddr.S_un.S_addr = (p->Data.A.IpAddress);
              wprintf(L"The IP address of %s is %S \n", p->pName, inet_ntoa(ipaddr));
              wprintf(L"TTL: %d (secs)\n",p->dwTtl);        

               p = p->pNext;       
       }

       if (pSrvList) LocalFree(pSrvList);

        DnsRecordListFree(ppQueryResultsSet, DnsFreeRecordList);

}

It turns out the flag that I needed was DNS_QUERY_WIRE_ONLY, this sent the query on the wire and bypassed the local cache.

/Jeff

Follow us on Twitter, www.twitter.com/WindowsSDK.