🎓 A1Learner
🔍
0 of 149 lessons completed

Rust DNS Lookups

A DNS lookup turns a name into one or more socket addresses. Rust exposes a DNS lookup through the ToSocketAddrs trait.

Turn a name into addresses

The call gives an iterator. More than one answer can come back:

let addrs = ("localhost", 8080).to_socket_addrs().unwrap();

Print every answer

This program sorts the answers first. The order on screen then stays steady:

let mut addrs: Vec<_> = ("localhost", 8080).to_socket_addrs().unwrap().collect();
addrs.sort();
for addr in &addrs {
    println!("{addr} loopback = {}", addr.ip().is_loopback());
}

Count the answers

let count = ("localhost", 8080).to_socket_addrs().unwrap().count();
println!("answers for localhost = {count}");

Connect using a name

connect takes the same forms. This client reaches a server on this machine, found by name:

let mut client = TcpStream::connect(format!("localhost:{}", port)).unwrap();
println!("peer is loopback = {}", client.peer_addr().unwrap().ip().is_loopback());

A written address converts to itself

No lookup happens for a literal address. It just becomes a SocketAddr:

let lit = "127.0.0.1:9000".to_socket_addrs().unwrap().next().unwrap();
println!("literal gives itself = {}", lit == "127.0.0.1:9000".parse().unwrap());

Example

use std::net::ToSocketAddrs;

fn main() {
    let mut addrs: Vec<_> = ("localhost", 80).to_socket_addrs().unwrap().collect();
    addrs.sort();
    for addr in &addrs {
        println!("{addr}");
    }
    println!("answers = {}", addrs.len());
    println!("each is loopback = {}", addrs.iter().all(|a| a.ip().is_loopback()));
}

Key points

  • ToSocketAddrs: the trait that turns a name or a written address into socket addresses
  • Use to_socket_addrs() on "localhost:8080" or ("localhost", 8080).
  • Both forms give an iterator of answers
  • One name can have several answers.
  • An IPv4 answer and an IPv6 answer often arrive together
  • connect and bind accept any ToSocketAddrs value.
  • That is why names work inside those calls with no extra code
  • A written address like "127.0.0.1:8080" needs no lookup.
  • It converts into itself

In this example, one lookup does all the work. ("localhost", 80) becomes two socket addresses. One is IPv4. One is IPv6. The sort puts them in a steady order for printing. Both are loopback addresses, because localhost is this machine. A public name would answer with public addresses instead. A client then tries the answers in order until one connects.

Note

Your program never talks to a DNS server itself.

It asks the operating system.

The system uses its hosts file and its DNS settings.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why does to_socket_addrs() hand back an iterator, not a single address?