Look Up DNS Addresses Without the Hassle

Resolve IP addresses into domain names without API calls or command-line utilities.

ClassAct

Languages: VB

Technologies: DNS | IP | Asynchronous callbacks

 

Look Up DNS Addresses Without the Hassle

Resolve IP addresses into domain names without API calls or command-line utilities.

 

By Ken Getz

 

If you've ever needed to resolve an IP address into a domain name, or vice versa (to turn a domain name into its corresponding IP address), you might have dug deep into the ugliness of the Win32 API and its support for TCP/IP and other fun Internet protocols. You might have given up, using a kludge solution such as shelling out to the operating system and using the NS utility from a command prompt.

 

But thanks to the System.Net namespace, these techniques no longer are necessary. System.Net provides a group of classes that let you resolve domain names, IP addresses, and more with ease. In this article, I'll show you how to take advantage of the IPAddress, IPHostEntry, and Dns classes. Armed with this knowledge, you can embark on Domain Name Service (DNS) lookups from within your own applications without needing to rely on API calls or command-line utilities.

 

Interact With the IPAddress Class

The IPAddress class allows you to interact programmatically with objects that represent Internet Protocol (IP) addresses. Basically, an IPAddress object does little more than represent a computer's IP address. But working with DNS without also handling IP addresses is difficult, so you really can't bypass this important little class.

 

Although you're used to looking at IP addresses in "dotted quad" format (such as 192.168.1.1, for example), IP addresses are really 32-bit integers whose bits normally end up twiddled into the standard format. In case you care (and I just know you do), the long integer corresponding to 192.168.1.1 is 16884422; in hex (base 16), that's &H0101A2C6. If you take each byte-sized chunk of this value and reverse the order - Internet addresses are always "big-endian," meaning the most significant byte has the lowest memory address, which is the opposite from how humans see things - you get C6 A2 01 01, which, you'll be amazed to find out, corresponds to the decimal values 192, 168, 1, and 1. Put the dots in, and you've got an IP address. Luckily, someone else normally does this ugly work for you.

 

Therefore, the IPAddress class simply contains a 32-bit integer value and supplies properties and methods to make your interactions with IP addresses simpler. To create a new instance of an IPAddress object, you can use the class's constructor, passing in a 32-bit integer:

Dim ip As New IPAddress(16884422)

 

You wouldn't normally do it this way, at least not often. Generally, you'll end up with an IP address either by retrieving information from somewhere else (as you'll see when you dig into the IPHostEntry and Dns classes later in this article) or by calling the shared Parse method of the IPAddress class, passing in a dotted-quad string:

 

Dim ip As IPAddress = IPAddress.Parse("198.162.1.1")

 

Once you have an IPAddress object, you can use the Address property to return the internal 32-bit integer or the ToString method to return the dotted-quad version of the address.

To demonstrate the IPAddress class, browse to the sample page IpAddressDemo.aspx. This page shows you the results of each member of the IPAddress class (see FIGURE 1).

 


FIGURE 1: The sample page, IpAddressDemo.aspx, allows you to try out all the members of the simple IPAddress class.

 

In addition to the Address property and ToString method, the AddressFamily property specifies the address family of the IP address (the type of addressing scheme used by a Socket instance to resolve the address), whose value is always InterNetwork. (See the AddressFamily enumeration for more information.)

 

The sample page uses this code to create an IPAddress object, using the text you enter into the text box on the page as a dotted-quad representation:

 

Dim ip As IPAddress = IPAddress.Parse(txtIP.Text)

 

The sample then fills in three text boxes, using the members you've seen so far:

 

' Instance methods/properties

txtAddress.Text = ip.Address.ToString()

txtAddressFamily.Text = ip.AddressFamily.ToString()

txtToString.Text = ip.ToString()

 

Because you're likely to work with some specific IP address, the IPAddress class provides four read-only fields corresponding to special IP addresses. FIGURE 1 shows the four fields, and here is the sample code that fills the page:

 

txtAny.Text = IPAddress.Any.ToString()

txtBroadcast.Text = IPAddress.Broadcast.ToString()

txtLoopback.Text = IPAddress.Loopback.ToString()

txtNone.Text = IPAddress.None.ToString()

 

The IPAddress class also provides three shared methods: HostToNetworkOrder, NetworkToHostOrder, and IsLoopback - none of which you're likely to use often. The first two, HostToNetworkOrder and NetworkToHostOrder, are convenience procedures that handle the differences in the way computers store multibyte values. Some computers store the most significant byte lowest in memory, and others store the least significant byte with the lowest memory. These methods convert multibyte values from the order used by the local computer to the standardized order used by the network. (If the .NET Framework were destined to run on only one platform - Windows - these methods wouldn't be necessary.) The IsLoopback method returns a Boolean value indicating whether the address you've specified is equal to the special Loopback field. I'm not sure why this particular check is required, other than that this comparison is one you're likely to perform often, and it might be more efficient for the .NET Framework to perform the comparison for you.

 

The sample page tests the three methods using this code. The HostToNetworkOrder and NetworkToHostOrder methods can accept any type of integer value - I've sent them the IP address, as a demonstration. To give the IsLoopback method a real spin, change the page's IPAddress value to 127.0.0.1; the IsLoopback text box should display True:

 

txtHostToNetworkOrder.Text = _

 IPAddress.HostToNetworkOrder(ip.Address).ToString()

txtNetworkToHostOrder.Text = _

 IPAddress.NetworkToHostOrder(ip.Address).ToString()

txtIsLoopback.Text = IPAddress.IsLoopback(ip).ToString()

 

Another interesting class is the IPHostEntry class, which associates a DNS host name with an array of aliases and matching IP addresses. This class is rarely used on its own - it's most often used as a helper class for the Dns class, which allows you to resolve domain names and/or IP addresses. The IPHostEntry class provides but three unique properties: AddressList contains an array of IPAddress objects associated with a host; Aliases contains an array of strings providing a list of aliases associated with a host; and HostName gets a string containing the name of a host.

 

For example, FIGURE 2 shows the results of calling the Dns class's GetHostByName method, passing in the text www.microsoft.com. This method returns an IPHostEntry object corresponding to the resolved search through public DNS for www.microsoft.com, and the AddressList property of the IPHostEntry object contains the five addresses shown in the figure.

 


FIGURE 2: Calling the GetHostByName method of the Dns class returns an IPHostEntry object whose AddressList property contains an array of IPAddress objects.

 

FIGURE 3 shows another example using the Dns class's GetHostByAddress method. This method returns an IPHostEntry object whose HostName and Aliases properties contain the host name (microsoft.com, in this example) and a list of aliases (the strings in parentheses, in FIGURE 3).

 


FIGURE 3: Calling the GetHostByAddress method of the Dns class returns an IPHostEntry object whose HostName and Aliases properties are filled in with information about the requested IP address.

 

Perform DNS Lookups With the Dns Class

The Dns class provides the most interesting functionality of all the classes I've described. This class provides only shared methods - you'll never need to instantiate a Dns object in order to use the class's functionality. The most useful methods include GetHostByName, which retrieves DNS information for the specified domain name (www.microsoft.com; see FIGURE 2) as an IPHostEntry instance; GetHostByAddress, which retrieves DNS information for the specified IP address ("207.46.197.102"; see FIGURE 3) as an IPHostEntry instance; Resolve, which resolves a DNS host name or IP address in dotted-quad notation to an IPHostEntry instance; and GetHostName, which gets a string containing the host name for the local computer.

 

All these methods are synchronous and might cause substantial blocking time on the current thread. The Dns class also provides asynchronous techniques for calling GetHostByName and Resolve (BeginGetHostByName, EndGetHostByName, BeginResolve, and EndResolve) that use the standard .NET asynchronous callback mechanisms. (I'll describe these in more detail later in the article.)

 

Starting at the top of FIGURE 3, the GetHostName button calls this code, which retrieves the host name of the current computer:

 

Private Sub Button1_Click( _

 ByVal sender As System.Object, _

 ByVal e As System.EventArgs) _

 Handles Button1.Click

 

  lblHostName.Text = Dns.GetHostName()

End Sub

 

The GetHostByName button in FIGURE 3 calls this procedure (with the error handling removed here for brevity):

 

Private Sub GetHostByName()

  Dim iph As IPHostEntry

  Dim strResults As String

 

  If txtHostName.Text <> "" Then

    iph = Dns.GetHostByName(txtHostName.Text)

    strResults = HandleAddressList(iph, _

     chkDottedQuad.Checked)

  End If

  lblResults.Text = strResults

End Sub

 

This procedure creates an IPHostEntry object filled in with DNS-retrieved information about the specified domain name. Given that object, the HandleAddressList procedure creates a list of IP addresses, using either dotted-quad format or integer values depending on the checked status of chkDottedQuad:

 

Private Function HandleAddressList( _

 ByVal IPh As IPHostEntry, _

 ByVal UseDottedQuad As Boolean) As String

 

  Dim ip As IPAddress

  Dim strAddress As String

  Dim sb As New StringBuilder()

 

  For Each ip In IPh.AddressList

    If UseDottedQuad Then

      strAddress = ip.ToString()

    Else

      strAddress = ip.Address.ToString()

    End If

 

    sb.Append(strAddress)

    sb.Append("
")

  Next

  Return sb.ToString()

End Function

 

The HandleAddressList procedure uses a StringBuilder object to store all the IP addresses, looping through the AddressList property of the IPHostEntry object to build up the results. The code uses either the ToString method or the Address property, depending on whether you want to see dotted-quad values or integers.

 

Entering an IP address and clicking on GetHostByAddress on the sample form (FIGURE 3) runs this procedure (with error handling removed):

 

Private Sub GetHostByAddress()

  Dim iph As IPHostEntry

  Dim strResults As String

 

  If txtHostName.Text <> "" Then

    iph = Dns.GetHostByAddress(txtHostName.Text)

    strResults = HandleNames(iph)

  End If

  lblResults.Text = strResults

End Sub

 

In this case, the code calls the GetHostByAddress method of the Dns class, given an IP address. It returns a filled-in IPHostEntry object, and the HandleNames procedure uses the HostName property and iterates through the Aliases array property to create a list of alias names:

 

Private Function HandleNames( _

 ByVal IPh As IPHostEntry) As String

 

  ' Given an IPHostEntry object, its

  ' names, and aliases.

  Dim ip As IPAddress

  Dim strName As String

  Dim sb As New StringBuilder()

   Dim strAlias As String

 

  sb.Append(IPh.HostName)

  sb.Append("
")

 

  For Each strAlias In IPh.Aliases

    sb.AppendFormat("({0})
", strAlias)

  Next

  Return sb.ToString()

End Function

 

Although the example didn't need it, you also can write code like this when calling the GetHostByAddress method. That is, the class provides two overloaded versions - one that accepts a dotted-quad string and one that accepts an IPAddress object:

 

Dim ip As IPAddress = IPAddress.Parse(txtHostName.Text)

iph = Dns.GetHostByAddress(ip)

 

The Resolve method allows you to specify either an IP address or a domain name, and the method retrieves an IPHostEntry object corresponding to the value you've specified. Be warned that you're likely to get slightly different results depending on the information you supply. For example, if you call Resolve and pass in www.microsoft.com, you'll get the five IP addresses shown in FIGURE 2, along with the host name www.microsoft.akadns.net and an alias name www.microsoft.com. If you specify one of the five IP addresses, Resolve returns an IPHostEntry object filled in with that one IP address, as well as the host name microsoft.com and three aliases: microsoft.net, www.domestic.microsoft.com, and www.us.microsoft.com.

 

Clicking on the Resolve button runs this procedure (with error handling removed):

 

Private Sub Resolve()

  Dim iph As IPHostEntry

  Dim strResults As String

 

  If txtHostName.Text <> "" Then

    iph = Dns.Resolve(txtHostName.Text)

    strResults = _

     HandleAddressList(iph, chkDottedQuad.Checked) & _

     "
" & _

     HandleNames(iph)

  End If

  lblResults.Text = strResults

End Sub

 

This procedure simply calls both HandleAddressList and HandleNames, given the same IPHostEntry object.

 

Asynchronously Speaking

If you need to call the GetHostByName or Resolve methods asynchronously, you can set up an asynchronous callback mechanism using the corresponding Begin/End pairs of procedures. The async callback pattern in .NET lets  you set up callbacks easily, although the callback is difficult to demonstrate visually from an ASP.NET page - when the callback occurs, indicating that the processing is complete, the page has been rendered already. If you want to test out the sample code, your best bet is to set a breakpoint in the callback procedure and verify that the code gets called back when the DNS lookup is complete.

 

As with any other async callback in .NET, the processing breaks down into two parts: the "begin" part and the "end" part. When you click on the Async Resolve button on the sample form, you run this code:

 

Private Sub ResolveAsync()

  Dim iph As IPHostEntry

  Dim acb As New AsyncCallback(AddressOf HandleCallback)

 

  If txtHostName.Text <> "" Then

    Dns.BeginResolve(txtHostName.Text, acb, Nothing)

  End If

End Sub

 

This code sets up an AsyncCallback object containing the address of the procedure you want to have called when the DNS lookup is complete so you can process the results. In this case, you want to run the code in the HandleCallback procedure. Then, you call the BeginResolve method, passing in the same parameter as before (the contents of the text box), along with the AsyncCallback object (so the .NET Framework knows which procedure to call when the DNS lookup is complete) and an optional object that can contain information about the current state. I've passed Nothing as the third parameter because there's no need to pass any extra information. At this point, your procedure is done - control returns back to whomever called this procedure, and the .NET Framework fires up a worker thread to handle the method request.

 

Once the DNS lookup is complete, the .NET Framework calls the HandleCallback procedure:

 

Private Sub HandleCallback(ByVal ar As IAsyncResult)

  Dim iph As IPHostEntry

  Dim strResults As String

  iph = Dns.EndResolve(ar)

  strResults = HandleAddressList(iph, _

   chkDottedQuad.Checked)

  ' Now do something with the address you got.

  ' strResults contains the results here.

End Sub

 

This procedure calls the EndResolve method, passing in the IAsyncResult object passed to the callback procedure as its parameter. (This object stores state information for the async operation and provides a synchronization object to allow threads to be signaled when the operation completes. You'll always pass an object that implements this interface as a parameter to a method that handles the "end" of an asynchronous operation.) The EndResolve method returns the IPHostEntry object you need, and the rest of the code manipulates that object. (The BeginGetHostByName and EndGetHostByName methods work the same way, and there's no explicit demo of these in the sample.)

 

It's worth noting that ASP.NET security will make it difficult for you to embark on DNS lookups on public sites. The .NET Framework documentation mentions you'll need to deal with the DnsPermission class to allow DNS lookups from an Internet zone (see the .NET Framework documentation for more information). My guess is you're more likely to want to perform lookups either internally or as a service that helps manage your sites (as opposed to providing a site that does lookups, such as the sample I've created for this article). For now, you can run the sample locally and have fun investigating the wild and wacky world of DNS.

 

The files referenced in this article are available for download.

 

Ken Getz is a senior consultant with MCW Technologies and splits his time between programming, writing, and training. He specializes in tools and applications written in Visual Studio .NET and Visual Basic, and he is co-author of Access 2002 Desktop Developer's Handbook  as well as the training materials for AppDev's VB .NET, ASP.NET, Access 97 and 2000, Visual Basic 5.0, and Visual Basic 6.0 classes. He frequently speaks at technical conferences and is a contributing editor for asp.netPRO magazine. E-mail Ken at mailto:[email protected]

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish