Merge commit from fork

This commit is contained in:
ZacharyZcR
2026-07-15 14:55:05 +08:00
committed by GitHub
parent 3d42a2c09c
commit 4b2a60a854
@@ -2,6 +2,8 @@ import type { Request, Response } from "express";
import express from "express"; import express from "express";
import https from "https"; import https from "https";
import http from "http"; import http from "http";
import { lookup } from "dns/promises";
import { BlockList, isIP } from "net";
import { homepageLogger } from "../../utils/logger.js"; import { homepageLogger } from "../../utils/logger.js";
export const homepageProxyRouter = express.Router(); export const homepageProxyRouter = express.Router();
@@ -15,10 +17,82 @@ const proxyCache = new Map<string, ProxyCacheEntry>();
const CACHE_SIZE = 50; const CACHE_SIZE = 50;
const FETCH_TIMEOUT_MS = 8000; const FETCH_TIMEOUT_MS = 8000;
function fetchJson(url: string): Promise<unknown> { const blockedAddresses = new BlockList();
for (const [network, prefix] of [
["0.0.0.0", 8],
["10.0.0.0", 8],
["100.64.0.0", 10],
["127.0.0.0", 8],
["169.254.0.0", 16],
["172.16.0.0", 12],
["192.168.0.0", 16],
["198.18.0.0", 15],
["224.0.0.0", 4],
["240.0.0.0", 4],
] as const) {
blockedAddresses.addSubnet(network, prefix, "ipv4");
}
for (const [network, prefix] of [
["::", 128],
["::1", 128],
["::ffff:0:0", 96],
["fc00::", 7],
["fe80::", 10],
["ff00::", 8],
] as const) {
blockedAddresses.addSubnet(network, prefix, "ipv6");
}
function isBlockedAddress(address: string): boolean {
const family = isIP(address);
return (
family === 0 ||
blockedAddresses.check(address, family === 4 ? "ipv4" : "ipv6")
);
}
async function resolvePublicUrl(rawUrl: string): Promise<{
url: URL;
address: string;
}> {
const url = new URL(rawUrl);
if (
!["http:", "https:"].includes(url.protocol) ||
url.username ||
url.password
) {
throw new Error("Invalid URL");
}
const hostname = url.hostname.replace(/^\[|\]$/g, "");
const addresses = isIP(hostname)
? [{ address: hostname }]
: await lookup(hostname, { all: true, verbatim: true });
if (
addresses.length === 0 ||
addresses.some(({ address }) => isBlockedAddress(address))
) {
throw new Error("Private destinations are not allowed");
}
return { url, address: addresses[0].address };
}
async function fetchJson(rawUrl: string): Promise<unknown> {
const { url, address } = await resolvePublicUrl(rawUrl);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const mod = url.startsWith("https") ? https : http; const mod = url.protocol === "https:" ? https : http;
const req = mod.get(url, { timeout: FETCH_TIMEOUT_MS }, (res) => { const req = mod.get(
{
protocol: url.protocol,
hostname: address,
port: url.port || undefined,
path: `${url.pathname}${url.search}`,
headers: { Host: url.host },
servername: url.protocol === "https:" ? url.hostname : undefined,
timeout: FETCH_TIMEOUT_MS,
},
(res) => {
const chunks: Buffer[] = []; const chunks: Buffer[] = [];
res.on("data", (chunk: Buffer) => chunks.push(chunk)); res.on("data", (chunk: Buffer) => chunks.push(chunk));
res.on("end", () => { res.on("end", () => {
@@ -30,7 +104,8 @@ function fetchJson(url: string): Promise<unknown> {
} }
}); });
res.on("error", reject); res.on("error", reject);
}); },
);
req.on("error", reject); req.on("error", reject);
req.on("timeout", () => { req.on("timeout", () => {
req.destroy(); req.destroy();