fix: sort IPv4 addresses before IPv6 in SSRF pinned DNS to fix Telegram media fetch on IPv6-broken hosts

On hosts where IPv6 is configured but not routed (common on cloud VMs),
Telegram media downloads fail because the pinned DNS lookup may return
IPv6 addresses first. Even though autoSelectFamily (Happy Eyeballs) is
enabled, the round-robin pinned lookup serves individual IPv6 addresses
that fail before IPv4 is attempted.

Sort resolved addresses so IPv4 comes first, ensuring both Happy Eyeballs
and single-address round-robin try the working address family first.

Fixes #23975

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Glucksberg
2026-02-23 02:21:34 +00:00
committed by Peter Steinberger
parent fb8edebc32
commit dd9ba974d0
2 changed files with 29 additions and 1 deletions

View File

@@ -155,6 +155,23 @@ describe("ssrf pinning", () => {
expect(lookup).not.toHaveBeenCalled();
});
it("sorts IPv4 addresses before IPv6 in pinned results", async () => {
const lookup = vi.fn(async () => [
{ address: "2001:db8::1", family: 6 },
{ address: "93.184.216.34", family: 4 },
{ address: "2001:db8::2", family: 6 },
{ address: "93.184.216.35", family: 4 },
]) as unknown as LookupFn;
const pinned = await resolvePinnedHostname("example.com", lookup);
expect(pinned.addresses).toEqual([
"93.184.216.34",
"93.184.216.35",
"2001:db8::1",
"2001:db8::2",
]);
});
it("allows ISATAP embedded private IPv4 when private network is explicitly enabled", async () => {
const lookup = vi.fn(async () => [
{ address: "2001:db8:1234::5efe:127.0.0.1", family: 6 },