Module domain::resolv [] [src]

An asynchronous stub resolver.

A resolver is the component in the DNS that answers queries. A stub resolver does so by simply relaying queries to a different resolver chosen from a predefined set. This is how pretty much all user applications use DNS.

This module implements a modern, asynchronous stub resolver built on top of tokio-core.

The module provides ways to create a resolver that knows how to process DNS queries. A query asks for all the resource records associated with a given triple of a domain name, resource record type, and class (known as a question). It is a future resolving to a DNS message with the response or an error. Queries can be combined into lookups that use the returned resource records to answer more specific enquiries such as all the IP addresses associated with a given host name. The module provides a rich set of common lookups in the lookup sub-module.

The following gives an introduction into using the resolver. For an introduction into the internal design, please have a look at the intro sub-module.

Creating a Resolver

The resolver is represented by the Resolver type. When creating a value of this type, you create all the parts of an actual resolver according to a resolver configuration. Since these parts are handling actual network traffic, the resolver needs a handle to a Tokio reactor into which these parts will be spawned as futures.

For the resolver configuration, there’s ResolvConf. While you can create a value of this type by hand, the more common way is to use your system’s resolver configuration. ResolvConf implements the Default trait doing exactly that by reading /etc/resolv.conf.

That probably won’t work on Windows, but, sadly, I have no idea how to retrieve the resolver configuration there. Some help here would be very much appreciated.

Since using the system configuration is the most common case by far, Resolver’s new() function does just that. So, the easiest way to get a resolver is just this:

use domain::resolv::Resolver;
use tokio_core::reactor::Core;

let core = Core::new().unwrap();
let resolv = Resolver::new(&core.handle()).unwrap();Run

If you do have a configuration, you can use the from_conf() function instead.

Using the Resolver: Queries

As was mentioned above, the Resolver does’t actually contain the networking parts necessary to answer queries. Instead, it only knows how to contact those parts. Because of this, you can clone the resolver, even pass it to other threads.

Oddly, the one thing you can’t do with a resolver is start a query. Instead, you need an intermediary type called ResolverTask. You’ll get one through Resolver::start() or, more correctly, you get a future to one through this method. You then chain on your actual query or sequence of queries using combinators such as Future::and_then().

The actual query is started through ResolverTask::query(). It takes a domain name, a resource record type, and a class and returns a future that will resolve into either a MessageBuf with the response to the query or an Error.

As an example, let’s find out the IPv6 addresses for www.rust-lang.org:

extern crate domain;
extern crate futures;
extern crate tokio_core;

use std::str::FromStr;
use domain::bits::DNameBuf;
use domain::iana::{Class, Rtype};
use domain::rdata::Aaaa;
use domain::resolv::Resolver;
use futures::Future;
use tokio_core::reactor::Core;

fn main() {
    let mut core = Core::new().unwrap();
    let resolv = Resolver::new(&core.handle()).unwrap();

    let addrs = resolv.start().and_then(|resolv| {
        let name = DNameBuf::from_str("www.rust-lang.org.").unwrap();
        resolv.query(name, Rtype::Aaaa, Class::In)
    });
    let response = core.run(addrs).unwrap();
    for record in response.answer().unwrap().limit_to::<Aaaa>() {
        println!("{}", record.unwrap());
    }
}Run

Note the final dot at "www.rust-lang.org." making it an absolute domain name. Queries don’t know how to deal with relative names and will error out if given one.

Complex Queries: Lookups

Most times when you are using DNS you aren’t really interested in a bunch of resource records, though, you want an answer to a more direct question. For instance, if you want to know the IP addresses for a host name, you don’t really care that you have to make a query for the A records and one for AAAA records for that host name. You want the addresses.

This is what lookups do. They take a ResolverTask and some additional information and turn that into a future of some specific result. So, to do lookups you have to follow the procedure using start() as given above but instead of calling query() inside the closure, you use one of the lookup functions from the lookup sub-module.

Using lookup_host(), the process of looking up the IP addresses becomes much easier. To update above’s example:

extern crate domain;
extern crate futures;
extern crate tokio_core;

use std::str::FromStr;
use domain::bits::DNameBuf;
use domain::resolv::Resolver;
use domain::resolv::lookup::lookup_host;
use futures::Future;
use tokio_core::reactor::Core;

fn main() {
    let mut core = Core::new().unwrap();
    let resolv = Resolver::new(&core.handle()).unwrap();

    let addrs = resolv.start().and_then(|resolv| {
        let name = DNameBuf::from_str("www.rust-lang.org").unwrap();
        lookup_host(resolv, name)
    });
    let response = core.run(addrs).unwrap();
    for addr in response.iter() {
        println!("{}", addr);
    }
}Run

No more fiddeling with record types and classes and the result can now iterate over IP addresses. And we get both IPv4 and IPv6 addresses to boot.

Furthermore, we now can use a relative host name. It will be turned into an absolute name according to the rules set down by the configuration we used when creating the resolver.

As an aside, the lookup functions are named after the thing they look up not their result following the example of the standard library. So, when you look for the addresses for the host, you have to use lookup_host(), not lookup_addr().

Have a look at the lookup module for all the lookup functions currently available.

The Run Shortcut

If you only want to do a DNS lookup and don’t otherwise use tokio, there is a shortcut through the Resolver::run() associated function. It takes a closure from a ResolverTask to a future and waits while driving the future to completing. In other words, it takes away all the boiler plate from above:

extern crate domain;

use std::str::FromStr;
use domain::bits::DNameBuf;
use domain::resolv::Resolver;
use domain::resolv::lookup::lookup_host;

fn main() {
    let response = Resolver::run(|resolv| {
        let name = DNameBuf::from_str("www.rust-lang.org").unwrap();
        lookup_host(resolv, name)
    });
    for addr in response.unwrap().iter() {
        println!("{}", addr);
    }
}Run

Reexports

pub use self::conf::ResolvConf;
pub use self::error::{Error, Result};

Modules

conf

Resolver configuration

error

Resolver errors and results.

hosts

Static host table.

intro

Resolver Architecture Documentation

lookup

Lookup functions and related types.

Structs

Query

A DNS query.

Resolver

Access to a resolver.

ResolverTask

A resolver bound to a futures task.