6989a98d75
- Arkitektur: docs/auth/passwordless-architecture.md - Backend: iom/quixzoom-auth-service/ (FastAPI + Redis) - Webb: quixzoom-market-pages/se/login/ (QR-kod + polling) - App: iom/quixzoom-app/src/features/auth/ (push + deep links) Flöde: QR-kod → app-godkännande → webb-inloggad
67 lines
2.4 KiB
JavaScript
67 lines
2.4 KiB
JavaScript
import { Debug } from '../debug.js';
|
|
const debug = Debug.extend('ipv6');
|
|
/**
|
|
* Normalize bracketed IPv6 URL targets into unbracketed host options.
|
|
*
|
|
* RFC 2732 defines the URL syntax for literal IPv6 addresses as bracketed
|
|
* host references (for example `http://[::1]:8080/path` where host is
|
|
* `[::1]`).
|
|
*
|
|
* `httpxy` resolves bracketed hostnames (for example `[::1]`) via DNS,
|
|
* which can fail for IPv6 literals. This converts string/URL `target` and
|
|
* `forward` values into object form with `hostname: ::1` (brackets removed)
|
|
* so the address can be connected directly.
|
|
*
|
|
* Reference: RFC 2732, Section 2 (Literal IPv6 Address Format in URL's)
|
|
* https://www.ietf.org/rfc/rfc2732.txt
|
|
*
|
|
* The provided options object is mutated in place.
|
|
*/
|
|
export function normalizeIPv6LiteralTargets(options) {
|
|
options.target = normalizeIPv6ProxyTarget(options.target, 'target');
|
|
options.forward = normalizeIPv6ProxyTarget(options.forward, 'forward');
|
|
}
|
|
function normalizeIPv6ProxyTarget(target, optionName) {
|
|
const targetUrl = toTargetUrl(target);
|
|
if (targetUrl && isBracketedIPv6Hostname(targetUrl.hostname)) {
|
|
const normalizedHostname = normalizeIPv6DestinationHostname(stripBrackets(targetUrl.hostname));
|
|
debug('normalized IPv6 "%s" %s', optionName, target);
|
|
const auth = targetUrl.username || targetUrl.password
|
|
? `${targetUrl.username}:${targetUrl.password}`
|
|
: undefined;
|
|
return {
|
|
hostname: normalizedHostname,
|
|
auth,
|
|
pathname: targetUrl.pathname,
|
|
port: targetUrl.port,
|
|
protocol: targetUrl.protocol,
|
|
search: targetUrl.search,
|
|
};
|
|
}
|
|
return target;
|
|
}
|
|
function toTargetUrl(target) {
|
|
if (typeof target === 'string') {
|
|
return new URL(target);
|
|
}
|
|
if (target instanceof URL) {
|
|
return target;
|
|
}
|
|
return undefined;
|
|
}
|
|
function isBracketedIPv6Hostname(hostname) {
|
|
return hostname.startsWith('[') && hostname.endsWith(']');
|
|
}
|
|
function stripBrackets(hostname) {
|
|
return hostname.replace(/^\[|\]$/g, '');
|
|
}
|
|
function normalizeIPv6DestinationHostname(hostname) {
|
|
// The unspecified address (::) is not a routable destination for outbound client requests.
|
|
// Treat it as loopback so a target like http://[::]:port reaches local IPv6 listeners.
|
|
if (hostname === '::') {
|
|
debug('normalizing hostname unspecified IPv6 address (::) to loopback (::1)');
|
|
return '::1';
|
|
}
|
|
return hostname;
|
|
}
|