From f5c3dff43ccc51427181d6a9646d6892efd51a97 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 20 Nov 2025 12:24:24 -0500 Subject: [PATCH] Some small bug fixes --- server/private/routers/hybrid.ts | 9 ++- .../routers/newt/handleNewtRegisterMessage.ts | 53 ++++++++++-------- server/routers/site/createSite.ts | 56 +++++++++++++++++++ 3 files changed, 94 insertions(+), 24 deletions(-) diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index d9178446..89188a1e 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -1219,7 +1219,7 @@ hybridRouter.post( ); const geoIpLookupParamsSchema = z.object({ - ip: z.string().ip() + ip: z.union([z.ipv4(), z.ipv6()]) }); hybridRouter.get( "/geoip/:ip", @@ -1772,7 +1772,12 @@ hybridRouter.post( tls: logEntry.tls })); - await db.insert(requestAuditLog).values(logEntries); + // batch them into inserts of 100 to avoid exceeding parameter limits + const batchSize = 100; + for (let i = 0; i < logEntries.length; i += batchSize) { + const batch = logEntries.slice(i, i + batchSize); + await db.insert(requestAuditLog).values(batch); + } return response(res, { data: null, diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 372f3677..ff89bd3d 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -1,8 +1,8 @@ -import { db, exitNodeOrgs, newts } from "@server/db"; +import { db, ExitNode, exitNodeOrgs, newts, Transaction } from "@server/db"; import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { targetHealthCheck } from "@server/db"; -import { eq, and, sql, inArray } from "drizzle-orm"; +import { eq, and, sql, inArray, ne } from "drizzle-orm"; import { addPeer, deletePeer } from "../gerbil/peers"; import logger from "@server/logger"; import config from "@server/lib/config"; @@ -151,27 +151,8 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { return; } - const sitesQuery = await db - .select({ - subnet: sites.subnet - }) - .from(sites) - .where(eq(sites.exitNodeId, exitNodeId)); + const newSubnet = await getUniqueSubnetForSite(exitNode); - const blockSize = config.getRawConfig().gerbil.site_block_size; - const subnets = sitesQuery - .map((site) => site.subnet) - .filter( - (subnet) => - subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) - ) - .filter((subnet) => subnet !== null); - subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); - const newSubnet = findNextAvailableCidr( - subnets, - blockSize, - exitNode.address - ); if (!newSubnet) { logger.error( `No available subnets found for the new exit node id ${exitNodeId} and site id ${siteId}` @@ -376,3 +357,31 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { excludeSender: false // Include sender in broadcast }; }; + +async function getUniqueSubnetForSite( + exitNode: ExitNode, + trx: Transaction | typeof db = db +): Promise { + const sitesQuery = await trx + .select({ + subnet: sites.subnet + }) + .from(sites) + .where(eq(sites.exitNodeId, exitNode.exitNodeId)); + + const blockSize = config.getRawConfig().gerbil.site_block_size; + const subnets = sitesQuery + .map((site) => site.subnet) + .filter( + (subnet) => + subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) + ) + .filter((subnet) => subnet !== null); + subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); + const newSubnet = findNextAvailableCidr( + subnets, + blockSize, + exitNode.address + ); + return newSubnet; +} diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index f98a01dc..f8152fec 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -202,6 +202,62 @@ export async function createSite( } } + if (subnet && exitNodeId) { + //make sure the subnet is in the range of the exit node if provided + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Exit node not found") + ); + } + + if (!exitNode.address) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Exit node has no subnet defined" + ) + ); + } + + const subnetIp = subnet.split("/")[0]; + + if (!isIpInCidr(subnetIp, exitNode.address)) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Subnet is not in the CIDR range of the exit node address." + ) + ); + } + + // lets also make sure there is no overlap with other sites on the exit node + const sitesQuery = await db + .select({ + subnet: sites.subnet + }) + .from(sites) + .where( + and( + eq(sites.exitNodeId, exitNodeId), + eq(sites.subnet, subnet) + ) + ); + + if (sitesQuery.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + `Subnet ${subnet} overlaps with an existing site on this exit node. Please restart site creation.` + ) + ); + } + } + const niceId = await getUniqueSiteName(orgId); let newSite: Site;