mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 18:31:33 +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 { describe, expect, it, vi } from "vitest";
|
||||||
import type { LookupAddress, LookupAllOptions } from "dns";
|
import type { LookupAddress, LookupAllOptions, LookupOptions } from "dns";
|
||||||
import {
|
import {
|
||||||
createDnsLookupHook,
|
createDnsLookupHook,
|
||||||
isBlockedAddress,
|
isBlockedAddress,
|
||||||
@@ -56,6 +56,7 @@ describe("isBlockedAddress", () => {
|
|||||||
function runHook(
|
function runHook(
|
||||||
addresses: LookupAddress[],
|
addresses: LookupAddress[],
|
||||||
error: NodeJS.ErrnoException | null = null,
|
error: NodeJS.ErrnoException | null = null,
|
||||||
|
options: LookupOptions = { all: true },
|
||||||
) {
|
) {
|
||||||
const fakeLookup = (
|
const fakeLookup = (
|
||||||
_host: string,
|
_host: string,
|
||||||
@@ -65,14 +66,43 @@ function runHook(
|
|||||||
|
|
||||||
const hook = createDnsLookupHook(fakeLookup);
|
const hook = createDnsLookupHook(fakeLookup);
|
||||||
const callback = vi.fn();
|
const callback = vi.fn();
|
||||||
hook("example.invalid", { all: true }, callback);
|
hook("example.invalid", options, callback);
|
||||||
return callback;
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("createDnsLookupHook", () => {
|
describe("createDnsLookupHook", () => {
|
||||||
it("allows a public IPv4 address through", () => {
|
it("returns every public address when all addresses are requested", () => {
|
||||||
const callback = runHook([{ address: "104.21.52.150", family: 4 }]);
|
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);
|
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", () => {
|
it("rejects a private address with the private-destination error", () => {
|
||||||
@@ -81,8 +111,7 @@ describe("createDnsLookupHook", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: "Private destinations are not allowed",
|
message: "Private destinations are not allowed",
|
||||||
}),
|
}),
|
||||||
"",
|
[],
|
||||||
0,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,8 +121,7 @@ describe("createDnsLookupHook", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: "DNS resolution returned no addresses",
|
message: "DNS resolution returned no addresses",
|
||||||
}),
|
}),
|
||||||
"",
|
[],
|
||||||
0,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,6 +130,6 @@ describe("createDnsLookupHook", () => {
|
|||||||
code: "ENOTFOUND",
|
code: "ENOTFOUND",
|
||||||
});
|
});
|
||||||
const callback = runHook([], dnsError);
|
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 { BlockList, isIP } from "net";
|
||||||
import { Agent } from "undici";
|
import { Agent } from "undici";
|
||||||
|
|
||||||
@@ -13,8 +18,8 @@ type DnsLookupFn = (
|
|||||||
|
|
||||||
type LookupHookCallback = (
|
type LookupHookCallback = (
|
||||||
error: NodeJS.ErrnoException | Error | null,
|
error: NodeJS.ErrnoException | Error | null,
|
||||||
address: string,
|
address: string | LookupAddress[],
|
||||||
family: number,
|
family?: number,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
const blockedAddresses = new BlockList();
|
const blockedAddresses = new BlockList();
|
||||||
@@ -71,28 +76,27 @@ export function isBlockedAddress(address: string): boolean {
|
|||||||
export function createDnsLookupHook(dnsLookup: DnsLookupFn = lookup) {
|
export function createDnsLookupHook(dnsLookup: DnsLookupFn = lookup) {
|
||||||
return function lookupHook(
|
return function lookupHook(
|
||||||
host: string,
|
host: string,
|
||||||
lookupOptions: LookupAllOptions,
|
lookupOptions: LookupOptions,
|
||||||
callback: LookupHookCallback,
|
callback: LookupHookCallback,
|
||||||
): void {
|
): void {
|
||||||
|
const fail = (error: NodeJS.ErrnoException | Error) => {
|
||||||
|
if (lookupOptions.all) return callback(error, []);
|
||||||
|
callback(error, "", 0);
|
||||||
|
};
|
||||||
|
|
||||||
dnsLookup(
|
dnsLookup(
|
||||||
host,
|
host,
|
||||||
{ ...lookupOptions, all: true, verbatim: true },
|
{ ...lookupOptions, all: true, verbatim: true },
|
||||||
(error, addresses) => {
|
(error, addresses) => {
|
||||||
if (error) return callback(error, "", 0);
|
if (error) return fail(error);
|
||||||
if (!addresses.length) {
|
if (!addresses.length) {
|
||||||
return callback(
|
return fail(new Error("DNS resolution returned no addresses"));
|
||||||
new Error("DNS resolution returned no addresses"),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (addresses.some(({ address }) => isBlockedAddress(address))) {
|
if (addresses.some(({ address }) => isBlockedAddress(address))) {
|
||||||
return callback(
|
return fail(new Error("Private destinations are not allowed"));
|
||||||
new Error("Private destinations are not allowed"),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
if (lookupOptions.all) return callback(null, addresses);
|
||||||
|
|
||||||
const selected = addresses[0];
|
const selected = addresses[0];
|
||||||
callback(null, selected.address, selected.family);
|
callback(null, selected.address, selected.family);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user