Search code examples
rustnetwork-programmingdns

Can I resolve a domain without a port?


Another library's function is requiring a host to be passed in as an std::net::IpAddr. For dynamic resolution, I'd like to use a domain name.

Rust does seem to provide a method for domain resolution to an IP, but it requires a port: to_socket_addrs on str

use std::net::{ToSocketAddrs};

fn main() {
    let socket_str = "localhost:12345";
    let mut addrs_iter = socket_str.to_socket_addrs().unwrap();
    let addr = addrs_iter.next().unwrap();
    dbg!(addr.ip()); // resolved IP
}

Because I'm doing ICMP requests, a port is not required.

I have not found a way to do resolution on just a domain name (and no port) within the standard library. Does such a function exist?


Solution

  • The reason Rust wants a host:port is because ToSocketAddrs is built on the C getaddrinfo() function, which also wants a (host, port).

    In the C world[1], hostname resolution is usually done through the POSIX getaddrinfo() method – which takes a port number (actually a service name string[2]) as a required parameter, resolves both parameters using their respective databases, and returns you a full C 'struct sockaddr' with both the address and port (and address family, and scope index, and other parameters) already filled in.

    When a hostname needs to be resolved for other purposes than regular socket communications, usually "0" is provided as the port number to getaddrinfo() and the rest of the returned sockaddr ignored. (I've also seen 9 – the 'discard' port – sometimes used for this purpose.)

    Since std::net::ToSocketAddrs is a very thin layer over getaddrinfo (from looking at Rust's source code), I would do the same: specify 0 as port number and ignore that part of the result.


    [1] Rust relies on getaddrinfo() instead of a pure-Rust DNS client because it interfaces with the OS name lookup logic – which often is part of the same C library – and will handle non-DNS names such as NetBIOS or /etc/hosts, which a direct DNS lookup wouldn't, as well as other parameters such as caching or "domain search" logic that a raw DNS client would need to handle on its own.

    [2] Although these days a numeric port is directly specified most of the time, the original intent of this API was that the system would translate not just host names but also service names, e.g. one might use getaddrinfo("info.cern.ch", "http") to connect to an HTTP server and the system would consult /etc/services or perhaps NIS to translate "http" to 80. This didn't work out, maybe as services kept appearing faster than client systems could be updated to know about them.