Skip to content

Preserve IPv6 zone identifier in prepared URLs (#6735)#7388

Open
mdali9821 wants to merge 1 commit intopsf:mainfrom
mdali9821:fix/6735-ipv6-zone-id
Open

Preserve IPv6 zone identifier in prepared URLs (#6735)#7388
mdali9821 wants to merge 1 commit intopsf:mainfrom
mdali9821:fix/6735-ipv6-zone-id

Conversation

@mdali9821
Copy link
Copy Markdown

Fixes #6735.

Problem

Since requests 2.32.0, requesting a link-local IPv6 URL such as

requests.get("https://[fe80::1%ens192]/redfish/v1", verify=False)

fails with [Errno -2] Name or service not known. The same URL works against urllib3 directly, and worked with requests 2.31.0.

**Root cause**
In PreparedRequest.prepare_url:

urllib3.util.parse_url normalizes both %ens192 and %25ens192 forms of the IPv6 zone identifier to the literal form, giving us host = '[fe80::1%ens192]' with a literal %.
The assembled URL is passed through requote_uri, whose unquote_unreserved helper treats %en as a malformed percent- escape ('en'.isalnum() is true but int('en', 16) raises ValueError), raises InvalidURL, and requote_uri falls back to quote(uri, safe=safe_without_percent) which percent-encodes the
literal % to %25. 3. urllib3 then hands the host fe80::1%25ens192 straight to getaddrinfo(), which cannot resolve a zone literally named %25ens192.

urllib3 alone works because the literal %ens192 survives all the way to getaddrinfo(), which accepts it per RFC 4007.

Fix
After requote_uri runs, restore the literal % inside the bracketed host. Because parse_url guarantees that host only ever contains a % inside a bracketed IPv6 authority (never in the path/query/ fragment), the replacement is anchored to the authority and cannot accidentally undo an intentional %25 elsewhere in the URL.

python
url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
 
if host and "%" in host:
    encoded_host = host.replace("%", "%25", 1)
    url = url.replace(encoded_host, host, 1)
 
self.url = url
10 lines of production code, no new helpers, no regex, no changes to requote_uri / unquote_unreserved semantics (their existing tests continue to pass unchanged).

Tests
tests/test_requests.py::TestRequests::test_ipv6_zone_id_is_preserved adds four parametrized cases:

- literal %ens192 form preserved end-to-end;
- RFC 6874 %25ens192 form normalized to literal form;
- zone id preserved alongside port, query, and fragment;
- plain IPv6 (no zone id) unaffected.

The existing test_requote_uri_with_unquoted_percents and test_unquote_unreserved cases continue to pass, confirming that percent handling outside the authority is unchanged.

**Relation to prior attempts**
Aware of prior attempts at this bug (#5257, #7065, #7088, #7111, #7193). This PR differs by:

- being localized to prepare_url onlyno new helpers in utils.py / adapters.py, no regex, no behavior changes to the existing quoting utilities;
- using the already-parsed host variable as the anchor for the fix-up, so the scope is provably limited to the bracketed IPv6 authority;
- including a parametrized regression test directly tied to the reported failure case in #6735.

requote_uri percent-encodes the literal '%' zone-id separator to '%25',
which urllib3 then forwards verbatim to getaddrinfo(). The kernel cannot
resolve a zone literally named '%25eth0', so link-local IPv6 URLs such
as https://[fe80::1%ens192]/ fail with [Errno -2] Name or service not
known since requests 2.32.0.

Restore the literal '%' inside the bracketed host after requoting.
urllib3.util.parse_url already normalizes both the literal ('%eth0')
and RFC 6874 ('%25eth0') forms to the literal form, so we only need
to undo the re-encoding that requote_uri performs on the authority.

Fixes psf#6735.
@Jah-yee

This comment has been minimized.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

requests 2.32.3 with IPv6 link local address fails with error: [Errno -2] Name or service not known

2 participants