Add license checks to JobScheduler and AuthoritativeDNSServer

This commit is contained in:
Owen
2026-09-10 14:13:58 -04:00
parent 7537d0d792
commit 94f2e579d1
2 changed files with 41 additions and 0 deletions
@@ -17,6 +17,7 @@ import { certificateService } from "./certificate-service";
import { privateConfig as config } from "#private/lib/config"; import { privateConfig as config } from "#private/lib/config";
import { dnsValidator } from "./dns-validator"; import { dnsValidator } from "./dns-validator";
import { domainReverifier } from "./domain-reverifier"; import { domainReverifier } from "./domain-reverifier";
import license from "#private/license/license";
// Backstop for runExclusive: no single job's own internal timeouts (e.g. // Backstop for runExclusive: no single job's own internal timeouts (e.g.
// certificate-service's per-cert issuance timeout) are relied on here. This // certificate-service's per-cert issuance timeout) are relied on here. This
@@ -44,6 +45,12 @@ export class JobScheduler {
label: string label: string
): () => Promise<void> { ): () => Promise<void> {
return async () => { return async () => {
if (!(await license.isUnlocked())) {
logger.debug(
`Skipping ${label} tick - license is not subscribed`
);
return;
}
if (state.active) { if (state.active) {
logger.debug( logger.debug(
`Skipping ${label} tick - previous run still in progress` `Skipping ${label} tick - previous run still in progress`
+34
View File
@@ -33,6 +33,7 @@ import * as dnsResolver from "dns";
import logger from "@server/logger"; import logger from "@server/logger";
import { listExitNodes } from "../exitNodes"; import { listExitNodes } from "../exitNodes";
import { rateLimitService } from "../rateLimit"; import { rateLimitService } from "../rateLimit";
import license from "#private/license/license";
type DNSRecord = { type DNSRecord = {
id: number; id: number;
@@ -57,6 +58,13 @@ export class AuthoritativeDNSServer {
private allDomains: Set<string> = new Set(); private allDomains: Set<string> = new Set();
private domainRefreshInterval: NodeJS.Timeout | null = null; private domainRefreshInterval: NodeJS.Timeout | null = null;
// Cached license/subscription status. license.isUnlocked() does a DB
// round-trip on every call, so it can't be checked per-query on a UDP
// server that may see very high query volume - instead it's polled on
// the same cadence as the domain set refresh and read from memory here.
private isLicensed: boolean = false;
private licenseRefreshInterval: NodeJS.Timeout | null = null;
// Cache for per-queryName zone resolution and SOA records // Cache for per-queryName zone resolution and SOA records
private authoritativeDomainCache: NodeCache = new NodeCache({ private authoritativeDomainCache: NodeCache = new NodeCache({
stdTTL: 300, stdTTL: 300,
@@ -121,6 +129,15 @@ export class AuthoritativeDNSServer {
return; return;
} }
if (!this.isLicensed) {
logger.debug(
"Refusing DNS query - license is not subscribed"
);
// REFUSED (rcode=5) indicates a policy refusal by this nameserver.
this.sendResponse(packet, [], rinfo, false, 5, []);
return;
}
const question = packet.questions[0]; const question = packet.questions[0];
const dnsRateLimit = config.getRawConfig().dns!.rate_limit; const dnsRateLimit = config.getRawConfig().dns!.rate_limit;
@@ -1027,6 +1044,15 @@ export class AuthoritativeDNSServer {
} }
} }
private async refreshLicenseStatus(): Promise<void> {
try {
this.isLicensed = await license.isUnlocked();
} catch (error) {
logger.error("Failed to refresh license status:", error);
this.isLicensed = false;
}
}
public async start(): Promise<void> { public async start(): Promise<void> {
await this.loadAllDomains(); await this.loadAllDomains();
this.domainRefreshInterval = setInterval(() => { this.domainRefreshInterval = setInterval(() => {
@@ -1035,6 +1061,11 @@ export class AuthoritativeDNSServer {
); );
}, 60_000); }, 60_000);
await this.refreshLicenseStatus();
this.licenseRefreshInterval = setInterval(() => {
this.refreshLicenseStatus();
}, 60_000);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.server.bind(this.port, (err?: Error) => { this.server.bind(this.port, (err?: Error) => {
if (err) { if (err) {
@@ -1050,6 +1081,9 @@ export class AuthoritativeDNSServer {
if (this.domainRefreshInterval) { if (this.domainRefreshInterval) {
clearInterval(this.domainRefreshInterval); clearInterval(this.domainRefreshInterval);
} }
if (this.licenseRefreshInterval) {
clearInterval(this.licenseRefreshInterval);
}
return new Promise((resolve) => { return new Promise((resolve) => {
this.server.close(() => { this.server.close(() => {
logger.info("DNS server stopped"); logger.info("DNS server stopped");