Skip to content

Commit

Permalink
Normalize IPs during localhost loopback proxying
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
pimterry committed Nov 27, 2024
1 parent c894ce1 commit 71d45aa
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/rules/passthrough-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CacheableLookup from 'cacheable-lookup';
import { CompletedBody, Headers } from '../types';
import { byteLength } from '../util/util';
import { asBuffer } from '../util/buffer-utils';
import { isLocalhostAddress } from '../util/socket-util';
import { isLocalhostAddress, normalizeIP } from '../util/socket-util';
import { CachedDns, dnsLookup, DnsLookupFunction } from '../util/dns';
import { isMockttpBody, encodeBodyBuffer } from '../util/request-utils';
import { areFFDHECurvesSupported } from '../util/openssl-compat';
Expand Down Expand Up @@ -366,7 +366,7 @@ export async function getClientRelativeHostname(
// effectively free. We ignore errors to delegate unresolvable etc to request processing later.
isLocalhostAddress(await dnsLookup(lookupFn, hostname).catch(() => null))
) {
return remoteIp;
return normalizeIP(remoteIp) as string | undefined;

// Note that we just redirect - we don't update the host header. From the POV of the target, it's still
// 'localhost' traffic that should appear identical to normal.
Expand Down
10 changes: 5 additions & 5 deletions src/util/socket-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export const isLocalIPv6Available = isNode
)
: true;

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


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

0 comments on commit 71d45aa

Please sign in to comment.