Using std crates to make a DNS request

I want to get the IP Address of a domain using std (getting the A record). dns_lookup takes too long to return an IP. I need a code example because I'm not very familiar with the std functions.

The standard library only has one way to make DNS requests, and that is to use dns_lookup.

But why does it take so long to get an IP Address

The documentation on this function reads a bit oddly to me, it is described as returning an iterator, but it returns a Vec?

It looks like dns_lookup defers to libc::getaddrinfo() (which doesn't necessarily perform a DNS query), which is also used in std via std::net::ToSocketAddrs:

use std::net::ToSocketAddrs;

fn main() {
    for addr in "google.com:80".to_socket_addrs().unwrap() {
        println!("{:?}", addr);
    }
}

As to why it's slow, it really depends on your OS and setup and has nothing to do with Rust.

The Rust binary is running in a Docker container with this OS gcr.io/distroless/cc and I'm testing it on a Debian 12 host machine.

Have you tested to see if it is faster to get an IP address using non-Rust programs in that docker container?

Probably not because of Rust, unfortunately.

dns_lookup itself is fairly conservative. Most of the heavy lifting is farmed off to libc, via getaddrinfo. If dns_lookup is slow, odds are that the nslookup command - which calls the libc resolver mechanism directly - is also slow, and if that's the case in your environment, then the problem is probably more due to the configuration of your network or the container than due to dns_lookup.

Common sources of trouble with DNS lookups include sending the request to the wrong server first and waiting for a timeout (often 30-90 seconds), sending a request to an IPv6 address first on a network that doesn't support IPv6 and having to wait for a timeout (ditto), trying to use UDP with a DNS server that only supports TCP (or vice versa) and having to wait for a timeout (tritto), or using a name server that is, itself, slow or far away.

You can get a loose sense of which it is using tools like strace (to see which system calls are actually taking the most time in your program), tcpdump and Wireshark (to capture and visualize the actual network traffic supporting name resolution), dig (to make DNS requests directly, without going through libc to do it), and others.

If the problem actually is dns_lookup, then std::net::ToSocketAddrs, as implemented by &str, is probably also slow, since it also calls the libc getaddrinfo entry point. This is another way to determine if the problem lies in Rust; if only one of them is slow, then dig deeper into that one, but if both are slow, it's probably in the underlying mechanism.

3 Likes

trying to use UDP with a DNS server that only supports TCP (or vice versa) and having to wait for a timeout (tritto), or using a name server that is, itself, slow or far away.

When using it in in docker and a home network there also may be several steps of delegation going on, each of which could have its own inefficiencies.

rust -> libc in container -> whatever docker does to forward dns lookups -> host resolver (libc? nss? systemd-resolved?) -> home router's dns proxy -> ISP's recursive resolver -> authoritative name servers

1 Like