File size: 1,362 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import dns from "node:dns/promises";
import { detectIpLiteralFamily, stripIpv6Brackets } from "./proxyFamily.ts";

export type FamilyLookupFn = (
  hostname: string
) => Promise<Array<{ address: string; family: number }>>;

const defaultLookup: FamilyLookupFn = (hostname) => dns.lookup(hostname, { all: true });

/**
 * Fail-closed guarantee for an IPv6-only (or IPv4-only) proxy given as a hostname:
 * refuse early if the hostname has no record in the required family. No-op for IP
 * literals (their family is intrinsic).
 */
export async function assertHostnameSupportsFamily(
  host: string,
  family: 4 | 6,
  lookupFn: FamilyLookupFn = defaultLookup
): Promise<void> {
  if (detectIpLiteralFamily(host) !== null) return;
  let records: Array<{ address: string; family: number }>;
  try {
    records = await lookupFn(stripIpv6Brackets(host));
  } catch (err) {
    throw new Error(
      `[ProxyFamily] DNS resolution failed for ${host}; refusing to egress (fail-closed): ${
        err instanceof Error ? err.message : String(err)
      }`
    );
  }
  const hasFamily = records.some((r) => r.family === family);
  if (!hasFamily) {
    throw new Error(
      `[ProxyFamily] Proxy host ${host} has no ${family === 6 ? "IPv6 (AAAA)" : "IPv4 (A)"} record; refusing ${
        family === 6 ? "IPv6" : "IPv4"
      }-only egress (fail-closed)`
    );
  }
}