Skip to content

Commit a9318af

Browse files
committed
Normalize IPs during localhost loopback proxying
This shouldn't be visible externally - it just affects internal address resolution logic. This is useful because of cacheable-lookup#85 - in Node v20.12+ IPv6 IP DNS resolution can fail in unusual ways, so we'd rather preserve IPv4 traffic in simple IPv4 format where possible. The 'better' solution for this would be cacheable-lookup correctly resolving this address as dns.lookup does, or some kind of wrapper that avoids resolving IPs entirely (it's just inefficient and asking for trouble really).
1 parent c894ce1 commit a9318af

File tree

2 files changed

+7
-7
lines changed

2 files changed

+7
-7
lines changed

src/rules/passthrough-handling.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import CacheableLookup from 'cacheable-lookup';
88
import { CompletedBody, Headers } from '../types';
99
import { byteLength } from '../util/util';
1010
import { asBuffer } from '../util/buffer-utils';
11-
import { isLocalhostAddress } from '../util/socket-util';
11+
import { isLocalhostAddress, normalizeIP } from '../util/socket-util';
1212
import { CachedDns, dnsLookup, DnsLookupFunction } from '../util/dns';
1313
import { isMockttpBody, encodeBodyBuffer } from '../util/request-utils';
1414
import { areFFDHECurvesSupported } from '../util/openssl-compat';
@@ -366,7 +366,7 @@ export async function getClientRelativeHostname(
366366
// effectively free. We ignore errors to delegate unresolvable etc to request processing later.
367367
isLocalhostAddress(await dnsLookup(lookupFn, hostname).catch(() => null))
368368
) {
369-
return remoteIp;
369+
return normalizeIP(remoteIp) as string | null;
370370

371371
// Note that we just redirect - we don't update the host header. From the POV of the target, it's still
372372
// 'localhost' traffic that should appear identical to normal.

src/util/socket-util.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ export const isLocalIPv6Available = isNode
3737
)
3838
: true;
3939

40-
// We need to normalize ips for comparison, because the same ip may be reported as ::ffff:127.0.0.1
41-
// and 127.0.0.1 on the two sides of the connection, for the same ip.
42-
const normalizeIp = (ip: string | null | undefined) =>
40+
// We need to normalize ips some cases (especially comparisons), because the same ip may be reported
41+
// as ::ffff:127.0.0.1 and 127.0.0.1 on the two sides of the connection, for the same ip.
42+
export const normalizeIP = (ip: string | null | undefined) =>
4343
(ip && ip.startsWith('::ffff:'))
4444
? ip.slice('::ffff:'.length)
4545
: ip;
@@ -49,7 +49,7 @@ export const isLocalhostAddress = (host: string | null | undefined) =>
4949
host === 'localhost' || // Most common
5050
host.endsWith('.localhost') ||
5151
host === '::1' || // IPv6
52-
normalizeIp(host)!.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) // 127.0.0.0/8 range
52+
normalizeIP(host)!.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) // 127.0.0.0/8 range
5353
);
5454

5555

@@ -68,7 +68,7 @@ export const isSocketLoop = (outgoingSockets: net.Socket[] | Set<net.Socket>, in
6868
// will be undefined. If so, we know they're not relevant to loops, so skip entirely.
6969
return false;
7070
} else {
71-
return normalizeIp(outgoingSocket.localAddress) === normalizeIp(incomingSocket.remoteAddress) &&
71+
return normalizeIP(outgoingSocket.localAddress) === normalizeIP(incomingSocket.remoteAddress) &&
7272
outgoingSocket.localPort === incomingSocket.remotePort;
7373
}
7474
});

0 commit comments

Comments
 (0)