Skip to content

hyper-util: SOCKS5 connector sends a bracketed IPv6 literal as a domain name #4206

Description

@ArthurRab

My Claude discovered this.

Fable:

Affects hyper-util 0.1.20 (src/client/legacy/connect/proxy/socks/v5/mod.rs), seen through reqwest 0.13.5 with a socks5:// proxy (local DNS mode).

What happens

reqwest resolves the target locally and builds the destination URI with the address; an IPv6 address is written in brackets, as a URI requires (https://[2606:4700::6810:102]:443). SocksV5 then takes dst.host(), which for an IPv6 authority still carries the brackets ([2606:4700::6810:102]), and does:

let address = match host.parse::<IpAddr>() {
    Ok(ip) => Address::Socket(SocketAddr::new(ip, port)),
    Err(_) => ... Address::Domain(host, port)

"[2606:4700::6810:102]".parse::<IpAddr>() fails, so the literal goes out as ATYP 0x03 (domain name) with the brackets in the string. A SOCKS5 server that joins host and port (tailscaled's does, with Go's net.JoinHostPort) ends up with [[2606:4700::6810:102]]:443 and fails with "missing port in address". IPv4 literals have no brackets, so they parse and go out as ATYP 0x01.

Fix

Strip the URI brackets before parsing the host as an IP address, so an IPv6 literal goes out as ATYP 0x04:

-        let address = match host.parse::<IpAddr>() {
+        let bare = host.strip_prefix('[').and_then(|h| h.strip_suffix(']')).unwrap_or(&host);
+        let address = match bare.parse::<IpAddr>() {
             Ok(ip) => Address::Socket(SocketAddr::new(ip, port)),

A test: a SocksV5 request to https://[::1]:443 must encode ATYP 0x04 with the 16 raw bytes, not ATYP 0x03 with the text [::1].

How it showed up

A Rust server fetching pages through a Tailscale exit node (tailscaled --socks5-server) with reqwest::Proxy::all("socks5://…") and a custom resolver: every host with an AAAA record failed with error sending request (cause: the proxy's "missing port in address"), and hosts with only A records worked. Ordering the resolver's answers IPv4-first works around it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions