ipv6.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { Debug } from '../debug.js';
  2. const debug = Debug.extend('ipv6');
  3. /**
  4. * Normalize bracketed IPv6 URL targets into unbracketed host options.
  5. *
  6. * RFC 2732 defines the URL syntax for literal IPv6 addresses as bracketed
  7. * host references (for example `http://[::1]:8080/path` where host is
  8. * `[::1]`).
  9. *
  10. * `httpxy` resolves bracketed hostnames (for example `[::1]`) via DNS,
  11. * which can fail for IPv6 literals. This converts string/URL `target` and
  12. * `forward` values into object form with `hostname: ::1` (brackets removed)
  13. * so the address can be connected directly.
  14. *
  15. * Reference: RFC 2732, Section 2 (Literal IPv6 Address Format in URL's)
  16. * https://www.ietf.org/rfc/rfc2732.txt
  17. *
  18. * The provided options object is mutated in place.
  19. */
  20. export function normalizeIPv6LiteralTargets(options) {
  21. options.target = normalizeIPv6ProxyTarget(options.target, 'target');
  22. options.forward = normalizeIPv6ProxyTarget(options.forward, 'forward');
  23. }
  24. function normalizeIPv6ProxyTarget(target, optionName) {
  25. const targetUrl = toTargetUrl(target);
  26. if (targetUrl && isBracketedIPv6Hostname(targetUrl.hostname)) {
  27. const normalizedHostname = normalizeIPv6DestinationHostname(stripBrackets(targetUrl.hostname));
  28. debug('normalized IPv6 "%s" %s', optionName, target);
  29. const auth = targetUrl.username || targetUrl.password
  30. ? `${targetUrl.username}:${targetUrl.password}`
  31. : undefined;
  32. return {
  33. hostname: normalizedHostname,
  34. auth,
  35. pathname: targetUrl.pathname,
  36. port: targetUrl.port,
  37. protocol: targetUrl.protocol,
  38. search: targetUrl.search,
  39. };
  40. }
  41. return target;
  42. }
  43. function toTargetUrl(target) {
  44. if (typeof target === 'string') {
  45. return new URL(target);
  46. }
  47. if (target instanceof URL) {
  48. return target;
  49. }
  50. return undefined;
  51. }
  52. function isBracketedIPv6Hostname(hostname) {
  53. return hostname.startsWith('[') && hostname.endsWith(']');
  54. }
  55. function stripBrackets(hostname) {
  56. return hostname.replace(/^\[|\]$/g, '');
  57. }
  58. function normalizeIPv6DestinationHostname(hostname) {
  59. // The unspecified address (::) is not a routable destination for outbound client requests.
  60. // Treat it as loopback so a target like http://[::]:port reaches local IPv6 listeners.
  61. if (hostname === '::') {
  62. debug('normalizing hostname unspecified IPv6 address (::) to loopback (::1)');
  63. return '::1';
  64. }
  65. return hostname;
  66. }