"Invalid Server Name" when requesting an IPv6 literal through a proxy
An HTTPS request to an IPv6 literal fails with "Invalid Server Name" when a proxy is configured. The same request to an IPv4 literal, or to a hostname, works.
Example code to reproduce this issue:
use tokio::io::{AsyncReadExt, AsyncWriteExt};
// A CONNECT proxy that accepts the tunnel and holds the socket open.
async fn proxy() -> std::net::SocketAddr {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
while let Ok((mut sock, _)) = listener.accept().await {
let mut buf = [0u8; 1024];
let _ = sock.read(&mut buf).await;
let _ = sock
.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n")
.await;
tokio::spawn(async move {
let mut sink = [0u8; 1024];
while sock.read(&mut sink).await.unwrap_or(0) != 0 {}
});
}
});
addr
}
#[tokio::main]
async fn main() {
let proxy = proxy().await;
let client = reqwest::Client::builder()
.proxy(reqwest::Proxy::https(format!("http://{proxy}")).unwrap())
.timeout(std::time::Duration::from_secs(2))
.build()
.unwrap();
for url in ["https://[2001:db8::1]/", "https://192.0.2.1/"] {
println!("{url}\n {:?}\n", client.get(url).send().await.unwrap_err());
}
}
[dependencies]
reqwest = { version = "0.13.4", features = ["rustls"] }
tokio = { version = "1", features = ["full"] }https://[2001:db8::1]/ -> reqwest::Error { kind: Request, url: "https://[2001:db8::1]/", source: hyper_util::client::legacy::Error(Connect, "Invalid Server Name") }
https://192.0.2.1/ -> reqwest::Error { kind: Request, url: "https://192.0.2.1/", source: TimedOut }
The IPv4 request reaches the TLS handshake and times out against the dummy proxy, which is expected. The IPv6 request never gets that far.
Why:
ServerName is built from dst.host() in the two proxy paths in src/connect.rs, and for an IPv6 literal that returns the bracketed form a URI requires. ServerName::try_from rejects it. The direct path is unaffected because HttpsConnector strips the brackets.
hyper-rustls fixed the same issue in rustls/hyper-rustls#181
Source: seanmonstar/reqwest