seanmonstar / reqwest

An easy and powerful Rust HTTP Client
https://docs.rs/reqwest
Apache License 2.0
9.95k stars 1.13k forks source link

Unable to connect to IPv6 hosts when enabling trust-dns and binding to a local IPv6 address #2049

Open nirvana-msu opened 12 months ago

nirvana-msu commented 12 months ago

Trust-dns changed default IP resolution strategy from Ipv4AndIpv6 to Ipv4thenIpv6, which means that if domain name has A record (IPv4), it will not attemt to query AAAA records (IPv6).

So if we bind reqwest client to a local IPv6 address, it will not be able to connect because trust-dns will not return any IPv6 records (if domain has both A and AAAA records)!

reqwest = { version = "^0.11.22", default-features = false, features = ["rustls-tls-webpki-roots", "trust-dns"] }
let client = reqwest::Client::builder()
  .local_address(ip)  // <- your IPv6 address here
  .timeout(Duration::from_secs(5))
  .build().unwrap();

client.get("https://ifconfig.me").send().await

returns error:

error sending request for url (https://ifconfig.me/): error trying to connect: tcp connect error: Network unreachable

As a workaround, connection does work if I override DNS resolution manually:

let client = reqwest::Client::builder()
  .local_address(ip)  // <- your IPv6 address here
  .resolve("ifconfig.me", SocketAddr::new("2600:1901:0:b2bd::".parse().unwrap(), 0))  // <- adding this makes it work
  .timeout(Duration::from_secs(5))
  .build().unwrap();

At best this is a very confusing behavior (expecially so when explicitly binding to a local IPv6 address!). It should be possible to connect over IPv6 with default settings,

P.S. Please ignore original issue description, I first thought the root cause was different.

nirvana-msu commented 12 months ago

The reason trust-dns still uses Ipv4thenIpv6 is given here, which is basically a performance concern and the lack of streaming API (which would allow to first return IPv4 records and then IPv6 if resolution takes longer).

Not sure what's the best way to address this, but when host is IPv6-only or if explicitly binding to a local IPv6 address, the resolver should query for AAAA records by default...