mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-23 14:36:41 +00:00
fix outbound DNS lookup callback shape (#1101)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { LookupAddress, LookupAllOptions } from "dns";
|
||||
import type { LookupAddress, LookupAllOptions, LookupOptions } from "dns";
|
||||
import {
|
||||
createDnsLookupHook,
|
||||
isBlockedAddress,
|
||||
@@ -56,6 +56,7 @@ describe("isBlockedAddress", () => {
|
||||
function runHook(
|
||||
addresses: LookupAddress[],
|
||||
error: NodeJS.ErrnoException | null = null,
|
||||
options: LookupOptions = { all: true },
|
||||
) {
|
||||
const fakeLookup = (
|
||||
_host: string,
|
||||
@@ -65,14 +66,43 @@ function runHook(
|
||||
|
||||
const hook = createDnsLookupHook(fakeLookup);
|
||||
const callback = vi.fn();
|
||||
hook("example.invalid", { all: true }, callback);
|
||||
hook("example.invalid", options, callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
describe("createDnsLookupHook", () => {
|
||||
it("allows a public IPv4 address through", () => {
|
||||
const callback = runHook([{ address: "104.21.52.150", family: 4 }]);
|
||||
expect(callback).toHaveBeenCalledWith(null, "104.21.52.150", 4);
|
||||
it("returns every public address when all addresses are requested", () => {
|
||||
const addresses = [
|
||||
{ address: "104.21.52.150", family: 4 },
|
||||
{ address: "2606:4700:3034::ac43:c88d", family: 6 },
|
||||
];
|
||||
const callback = runHook(addresses);
|
||||
expect(callback).toHaveBeenCalledWith(null, addresses);
|
||||
});
|
||||
|
||||
it.each<LookupOptions>([{ all: false }, {}])(
|
||||
"returns one public address for single-address lookup options %j",
|
||||
(options) => {
|
||||
const callback = runHook(
|
||||
[{ address: "104.21.52.150", family: 4 }],
|
||||
null,
|
||||
options,
|
||||
);
|
||||
expect(callback).toHaveBeenCalledWith(null, "104.21.52.150", 4);
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects a mixed result containing a mapped private address", () => {
|
||||
const callback = runHook([
|
||||
{ address: "104.21.52.150", family: 4 },
|
||||
{ address: "::ffff:127.0.0.1", family: 6 },
|
||||
]);
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: "Private destinations are not allowed",
|
||||
}),
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects a private address with the private-destination error", () => {
|
||||
@@ -81,8 +111,7 @@ describe("createDnsLookupHook", () => {
|
||||
expect.objectContaining({
|
||||
message: "Private destinations are not allowed",
|
||||
}),
|
||||
"",
|
||||
0,
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -92,8 +121,7 @@ describe("createDnsLookupHook", () => {
|
||||
expect.objectContaining({
|
||||
message: "DNS resolution returned no addresses",
|
||||
}),
|
||||
"",
|
||||
0,
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -102,6 +130,6 @@ describe("createDnsLookupHook", () => {
|
||||
code: "ENOTFOUND",
|
||||
});
|
||||
const callback = runHook([], dnsError);
|
||||
expect(callback).toHaveBeenCalledWith(dnsError, "", 0);
|
||||
expect(callback).toHaveBeenCalledWith(dnsError, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { lookup, type LookupAddress, type LookupAllOptions } from "dns";
|
||||
import {
|
||||
lookup,
|
||||
type LookupAddress,
|
||||
type LookupAllOptions,
|
||||
type LookupOptions,
|
||||
} from "dns";
|
||||
import { BlockList, isIP } from "net";
|
||||
import { Agent } from "undici";
|
||||
|
||||
@@ -13,8 +18,8 @@ type DnsLookupFn = (
|
||||
|
||||
type LookupHookCallback = (
|
||||
error: NodeJS.ErrnoException | Error | null,
|
||||
address: string,
|
||||
family: number,
|
||||
address: string | LookupAddress[],
|
||||
family?: number,
|
||||
) => void;
|
||||
|
||||
const blockedAddresses = new BlockList();
|
||||
@@ -71,28 +76,27 @@ export function isBlockedAddress(address: string): boolean {
|
||||
export function createDnsLookupHook(dnsLookup: DnsLookupFn = lookup) {
|
||||
return function lookupHook(
|
||||
host: string,
|
||||
lookupOptions: LookupAllOptions,
|
||||
lookupOptions: LookupOptions,
|
||||
callback: LookupHookCallback,
|
||||
): void {
|
||||
const fail = (error: NodeJS.ErrnoException | Error) => {
|
||||
if (lookupOptions.all) return callback(error, []);
|
||||
callback(error, "", 0);
|
||||
};
|
||||
|
||||
dnsLookup(
|
||||
host,
|
||||
{ ...lookupOptions, all: true, verbatim: true },
|
||||
(error, addresses) => {
|
||||
if (error) return callback(error, "", 0);
|
||||
if (error) return fail(error);
|
||||
if (!addresses.length) {
|
||||
return callback(
|
||||
new Error("DNS resolution returned no addresses"),
|
||||
"",
|
||||
0,
|
||||
);
|
||||
return fail(new Error("DNS resolution returned no addresses"));
|
||||
}
|
||||
if (addresses.some(({ address }) => isBlockedAddress(address))) {
|
||||
return callback(
|
||||
new Error("Private destinations are not allowed"),
|
||||
"",
|
||||
0,
|
||||
);
|
||||
return fail(new Error("Private destinations are not allowed"));
|
||||
}
|
||||
if (lookupOptions.all) return callback(null, addresses);
|
||||
|
||||
const selected = addresses[0];
|
||||
callback(null, selected.address, selected.family);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user