Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Addresses

Resolve a hostname to socket addresses

std-badge cat-net-badge

Real config files don’t carry IP addresses, they carry hostnames like api.internal:8080. ToSocketAddrs turns those strings into an iterator of SocketAddr you can connect to. It’s the same trait TcpStream::connect calls under the hood when handed a string.

A hostname can resolve to multiple addresses — at least one IPv4 and one IPv6 on any modern host. Walk all of them when you want to display every option, or take the first when one working address is enough.

use std::io;
use std::net::ToSocketAddrs;

fn main() -> io::Result<()> {
    for addr in "localhost:443".to_socket_addrs()? {
        println!("localhost:443 → {addr}");
    }
    Ok(())
}

Classify an IP address

std-badge cat-net-badge

Before connecting to an address pulled from user input, a config file, or an HTTP redirect, check what kind it is. Loopback, link-local, and RFC1918 private ranges have no business being targets for outbound traffic from a server exposed on the public internet — letting them through is the SSRF vector.

IpAddr exposes is_loopback, is_multicast, and is_unspecified on both v4 and v6. is_private and is_link_local are on Ipv4Addr only — match the variant to reach them.

use std::net::IpAddr;
use std::str::FromStr;

fn classify(addr: IpAddr) -> &'static str {
    if addr.is_loopback() { return "loopback"; }
    if addr.is_multicast() { return "multicast"; }
    if addr.is_unspecified() { return "unspecified"; }
    if let IpAddr::V4(v4) = addr {
        if v4.is_private() { return "private (RFC1918)"; }
        if v4.is_link_local() { return "link-local"; }
    }
    "public"
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let samples = ["127.0.0.1", "10.0.0.5", "169.254.1.1", "8.8.8.8", "224.0.0.1", "::1"];
    for s in samples {
        let addr = IpAddr::from_str(s)?;
        println!("{addr}: {}", classify(addr));
    }
    Ok(())
}

Bind for both IPv4 and IPv6

std-badge cat-net-badge

0.0.0.0 accepts IPv4 clients only. To accept both protocols with a single listener, bind to the unspecified IPv6 address [::]. On Linux and macOS the listener accepts IPv6 clients natively and IPv4 clients via IPv4-mapped addresses (::ffff:1.2.3.4).

On Windows and FreeBSD the IPV6_V6ONLY socket option defaults to true, so the same bind accepts IPv6 only. std::net doesn’t expose IPV6_V6ONLY; the socket2 crate does, or you can open two listeners.

use std::io;
use std::net::TcpListener;

fn main() -> io::Result<()> {
    let listener = TcpListener::bind("[::]:0")?;
    let addr = listener.local_addr()?;
    println!("listening on {addr}");
    Ok(())
}