< Query A Service & Form A Query | RnR Main | HOSTENT, gethostbyname & gethostbyaddr Example >
What do we have in this chapter 8 part 5?
|
Querying DNS
Previously we mentioned that the DNS name space is static, which means you cannot dynamically register your service; however, you can still use the Winsock name resolution functions to perform a DNS query. Performing a DNS query is actually a bit more complicated than performing a normal query for a service that you have registered because the DNS name space provider returns the query information as a BLOB. Why does it do this? Remember from the Chapter 3 discussion of gethostbyname() that a name lookup returns a HOSTENT structure that contains not only IP addresses but also aliases. That information doesn't quite fit into the fields of the WSAQUERYSET structure. The tricky aspect about BLOB data is that its data format is not well documented, which makes directly querying DNS challenging. First, let's take a look at how to open the query. The following code example tries to demonstrate how to query the DNS directly. Create a new empty Win32 console application project and add the project/solution name.
|
Add the following source code.
// Description:
// This sample illustrates how to use the WSALookupServiceXXX
// functions with the DNS name space. Because the DNS name
// space is static, we can't dynamically register our services
// with it; however, we can query it using these functions.
// This sample shows how to do this using the special GUID
// which corresponds to the gethostbyname() function.
//
// Command line argument
// Dnsquery hostname
// hostname Name of host to resolve
//
// Link to ws2_32.lib
#include <winsock2.h>
#include <ws2tcpip.h>
#include <SvcGuid.h>
#include <stdio.h>
#define RNR_BUFFER_SIZE (sizeof(WSAQUERYSET) + sizeof(struct hostent) + 1024)
// Function: FixList
// Description:
// This function takes the array of points within the HOSTENT
// BLOB returned from the WSALookupServiceNext function and
// corrects them. When the BLOB is returned the pointers
// are actually offsets from the start of the HOSTENT
// structure. We need to follow the array of pointers and
// make them absolute addresses until we hit the array
// entry with a zero in it.
VOID FixList(PCHAR ** List, PCHAR Base)
{
if (*List)
{
PCHAR * Addr;
Addr = *List = (PCHAR *)( ((DWORD)*List + Base) );
while(*Addr)
{
*Addr = (PCHAR)(((DWORD)*Addr + Base));
Addr++;
}
}
}
// Function: UnpackHostEnt
// Description:
// This function calls the FixList function on the two
// arrays of pointers. Otherwise it simply fixes the
// h_name field of the HOSTENT so its an absolute
// pointer rather than an offset
VOID UnpackHostEnt(struct hostent * hostent)
{
PCHAR pch;
pch = (PCHAR)hostent;
if(hostent->h_name)
{
// To make an absolute address, add the base address of
// the HOSTENT to the offset value and put it back
hostent->h_name = (PCHAR)((DWORD)hostent->h_name + pch);
}
FixList(&hostent->h_aliases, pch);
FixList(&hostent->h_addr_list, pch);
}
// Function: main
// Description:
// Initialize Winsock and set up the WSAQUERYSET structure
// for querying DNS. We use the special GUID
// SVCID_INET_HOSTADDRBYNAME for name resolution and we
// query on the UDP and TCP protocols. The information
// is returned as a BLOB so we must specify the LUP_RETURN_BLOB
// flag. Once the call to WSALookupServiceBegin/Next is
// successful then decode the BLOB data into a real HOSTENT
// and print the results.
int main(int argc, char **argv)
{
WSADATA wsd;
WSAQUERYSET *qs=NULL;
char buff[RNR_BUFFER_SIZE];
GUID HostnameGuid = SVCID_INET_HOSTADDRBYNAME;
HANDLE hRnr;
AFPROTOCOLS afproto[2] = { {AF_INET, IPPROTO_UDP}, {AF_INET, IPPROTO_TCP} };
DWORD dwLength=RNR_BUFFER_SIZE;
int ret, i;
LPBLOB pvRet=NULL;
HOSTENT *hp=NULL;
SOCKADDR_IN addr;
// Check for proper usage
if (argc != 2)
{
printf("Usage: %s hostname\n", argv[0]);
printf("Example: %s www.google.com\n", argv[0]);
return -1;
}
// Load Winsock
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("WSAStartup() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAStartup() is OK!\n");
// Initialize the WSAQUERYSET structure for the query
qs = (WSAQUERYSET *)buff;
memset(qs, 0, sizeof(*qs));
qs->dwSize = sizeof(WSAQUERYSET);
qs->lpszServiceInstanceName = (LPWSTR)argv[1]; // name to resolve
qs->lpServiceClassId = &HostnameGuid;
qs->dwNameSpace = NS_ALL; // or ND_DNS
qs->dwNumberOfProtocols = 2; // TCP and UDP
qs->lpafpProtocols = afproto;
ret = WSALookupServiceBegin(qs, LUP_RETURN_BLOB | LUP_RETURN_NAME, &hRnr);
if (ret == NO_ERROR)
{
printf("WSALookupServiceBegin() is OK!\n");
// We only have to call WSALookupServiceNext once because
// the BLOB HOSTENT return will contain multiple addresses
// if there are multiple IP addresses associated with the given name
qs = (WSAQUERYSET *) malloc(sizeof(WSAQUERYSET) + 100);
dwLength = sizeof(WSAQUERYSET) + 100;
ret = WSALookupServiceNext(hRnr, 0, &dwLength, qs);
if (ret != NO_ERROR)
{
// WSASERVICE_NOT_FOUND == 10108
if (WSAGetLastError() == WSASERVICE_NOT_FOUND)
{
printf("No such name found!\n");
WSALookupServiceEnd(hRnr);
return -1;
}
else
printf("The given hostname found!\n");
printf("WSALookupServiceNext() failed with error code %d\n", WSAGetLastError());
}
else
printf("WSALookupServiceNext() should be OK!\n");
if(WSALookupServiceEnd(hRnr) == 0)
printf("Handle to hRnr was released!\n");
else
printf("Failed to release hRnr handle, error %d\n", WSAGetLastError());
}
else
printf("WSALookupServiceBegin() failed with error code %d\n", WSAGetLastError());
// Make sure we got something returned
if (qs->lpBlob == NULL)
{
printf("qs->lpBlob returned NULL lol!\n");
return -1;
}
else
printf("qs->lpBlob returned something lol!\n");
// Create our own buffer to hold the blob data
hp = (HOSTENT *)LocalAlloc(LPTR, qs->lpBlob->cbSize);
if (!hp)
{
printf("LocalAlloc() for HOSTENT failed with error code %d\n", GetLastError());
return -1;
}
else
printf("LocalAlloc() for HOSTENT is OK!\n");
memcpy(hp, qs->lpBlob->pBlobData, qs->lpBlob->cbSize);
// Unpack the BLOB data so its meaningful
UnpackHostEnt(hp);
// Print out the addresses
for(i=0; hp->h_addr_list[i] ;i++)
{
memcpy(&addr.sin_addr, hp->h_addr_list[i], hp->h_length);
printf("%s IP: %s\n", argv[1], inet_ntoa(addr.sin_addr));
}
for(i=0; hp->h_aliases[i] ;i++)
printf("Alias: %s\n", hp->h_aliases[i]);
// Cleanup
LocalFree(hp);
if(WSACleanup() == 0)
printf("WSACleanup() is OK!\n");
else
printf("WSACleanup() failed with error code %d\n", WSAGetLastError());
return 0;
}
Build and run the project.
----------------------------------------------------
The WSANO_DATA (11004) defined that the name is valid but no data record for the requested type. The requested name is valid and was found in the database, but it does not have the correct associated data being resolved for. The usual example for this is a host name-to-address translation attempt (using gethostbyname() or WSAAsyncGetHostByName()) which uses the DNS (Domain Name Server). An MX record is returned but no A record, indicating the host itself exists, but is not directly reachable. However, in our program, it is weird lor!
< Query A Service & Form A Query | RnR Main | HOSTENT, gethostbyname & gethostbyaddr Example >