将 `http::uri::Uri` 的主机解析为 IP 地址

Resolve host of `http::uri::Uri` to IP addresses

我有一个 http::uri::Uri 值,我想获取主机的 IpAddr。如果主机是 IP 地址(例如 http://127.0.0.1:8080),则只需要对其进行解析。但如果是主机名,则需要进行 DNS 解析。我该怎么做最好?

似乎 std 只有一种 public 方法来解析主机:ToSocketAddrs。不幸的是,它包含 API 中与 DNS 解析无关的端口。但是哦,好吧。但是有了这个,我的第一次尝试是:

use std::net::ToSocketAddr;

let host = uri.authority().unwrap().host();
let addresses = (host, 0).to_socket_addrs()?
    .map(|socket_addr| socket_addr.ip());

(我在这里使用了一个虚拟端口,因为它与 DNS 解析无关。)

这种尝试适用于许多情况,但不适用于方括号 IPv6 形式:http://[::1]:8080。这里,host()[::1] 并且不能解析为 IpAddr,因此 to_socket_addrs() 尝试将 [::1] 解析为主机,但失败了。所以我需要手动检查主机是否是方括号内的 IPv6。不太好,尤其是因为我不知道这里究竟允许使用哪些语法。

我还有其他几个想法,比如将 Authority 更改为始终有一个虚拟端口,因为这样我就可以调用 authority.as_str().to_socket_addrs()<&str as ToSocketAddr> 确实支持 [::1]:8080 语法!但是如果有none.

,那意味着需要一个端口并且失败

是的,我还没有找到一种简单的方法来做到这一点。但这似乎不应该那么难!有什么想法吗?

首先尝试将其解析为 std::net::IpAddr,如果失败则查找主机名。您必须自己处理方括号符号,但这还不错:

use std::net::IpAddr;

fn parse_ip_from_uri_host(host: &str) -> Option<IpAddr> {
    host.parse::<IpAddr>().ok().or_else(||
        // Parsing failed, try as bracketed IPv6
        host.strip_prefix("[")?
            .strip_suffix("]")?
            .parse::<IpAddr>().ok()
    )
}

(Playground)

如果这样 returns None 则解析失败,下一步是尝试通过 DNS 将字符串解析为主机名。