DNS

The project contains a DNS implementation following RFC 1035. The following contains a quick introduction to this feature and some basic code snippets. If you're interested in a full sample that goes beyond the snippets shown here, download the current source code of the project which contains a sample project.

Why?

Windows Phone only supports an extremely limited subset of DNS. In particular, you can use the DeviceNetworkInformation class to resolve host names to IP addresses. However, DNS offers a lot more features than that, for example querying for MX records or similar data, which is not supported. The implementation of this projects is a good foundation to make use of these additional DNS features.

How

All the DNS related features are located in the namespace PhoneNetTools.Dns. The central class is DnsResolver. This class allows you to send a generic DNS message to a name server and consume the result. The methods used for this follow the async programming model and return an IAsyncResult. Typically, you would pass in a callback that is invoked when the operation is finished.

Using this class requires knowledge about the DNS protocol details so you can set the required parameters correctly. A request for a host name entry for example would look like this:

// create message
var message = new Message();
message.IsRecursionDesired = true;

// create question
var question = new Question(QType.A);
question.Name = hostName;
message.Questions.Add(question);

// let the resolver do the work
var resolver = new DnsResolver();
var ar = resolver.BeginDnsRequest(message, callback, state);

In your callback, you can then use the EndInvoke counterpart to get the result of the operation:

// use the async result to end the operation
var message = ar.EndInvoke();

// check to see if the message is a valid result
if (message.ResponseCode != ResponseCode.NoError)
{
    // do something with this error information
	// ...
}

// get the result
var result = message.AnswerRecords
    .OfType<ResourceRecordA>()
    .Select(answer => answer.IPAddress)
    .ToList();

// do something with the result (a collection of IP addresses)

A convenience wrapper

The Dns namespace also contains a convenient class named DnsHelper that provides more high-level methods to spare you the DNS details. For example, it's BeginGetHostEntry method sets up the required DNS parameters for you and in turn uses the DnsResolver to perform the query. The DnsHelper methods also implement the async programming model and work the same like the DnsResolver or other components you may know that use APM.

private DnsHelper _dns = new DnsHelper();

// [...]

_dns.BeginGetHostEntry(HostNameTextBox.Text, GetHostEntry_Callback, null);

// [...]

private void GetHostEntry_Callback(IAsyncResult ar)
{
    // this comes from a background thread.
    // you need to marshal it back to the UI thread if you want to access UI elements (also when you use MessageBox like below),
    // or synchronize access to shared resources to avoid threading issues.
    if (!CheckAccess())
    {
        Dispatcher.BeginInvoke(() => GetHostEntry_Callback(ar));
        return;
    }

    try
    {
        // simply call the "EndXYZ" method, passing on the async result,
        // to get the results. If there are pending exceptions, calling this
        // method will throw them
        var addresses = _dns.EndGetHostEntry(ar);

        // in our case, we simply concatenate the returned addresses (there
        // are potentially more than one) and show them.
        if (addresses != null)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var address in addresses)
            {
                sb.AppendLine(address.ToString());
            }
            MessageBox.Show("The obtained IP address(es): " + Environment.NewLine + sb);
        }
        else
        {
            MessageBox.Show("Unfortunately, the server did not return any addresses.");
        }
    }
    catch (Exception ex)
    {
        // you can test for more specific exceptions here,
        // for example if a server error was returned, then the exception thrown
        // will be of type DnsException and contain the response code from the server
        // with more details.
        MessageBox.Show("An error occurred: " + ex.Message);
    }
}

Things to note here are that:
  • The callback is invoked on a background thread, so you need to marshal back to the UI thread if you want to access UI elements.
  • EndInvoke throws pending exceptions that occurred during the operation, so make sure to catch them (this should be nothing new to you if you're familiar with the APM).
  • The thrown exceptions typically are of type SocketException in case of network errors, or the custom DnsException type if the response from the server indicated an error.

Specifying the name servers to use

The DnsResolver class maintains a static list of name servers that are used. By default, it adds the two OpenDNS name servers to its collection. If you don't like that or want to use your own server(s), you can simply clear the collection and/or add additional servers to it:

// get the list of used name servers
var nameServers = DnsResolver.NameServers; // returns a list of IPAddress instances

// clear the list
DnsResolver.ClearNameServers();

// add a custom server
DnsResolver.AddNameServer(myServersIPAddress);

The DnsResolver (and hence also the DnsHelper) randomly selects one of the entries of the name server collection to make its requests.

Known limitations

Only a small subset of the resource records defined in RFC 1035 is implemented at the moment. However, the library is able to handle all returned resource records and maps them to the Dns.Records.GenericResourceRecord type. The data specific to the resource record then is located in its Data property (a byte array). If you want more convenient access and interpret the data according to the definitions in RFC 1035, you would derive from GenericResourceRecord and add your logic to that derived class (for a sample take a look at the ResourceRecordA class). If you want to contribute an implementation for a resource record or request a particular implementation, please contact me.

Last edited Nov 11, 2011 at 11:03 AM by Mister_Goodcat, version 1

Comments

No comments yet.