import * as plugins from '../plugins.js'; import type { DcRouter } from '../classes.dcrouter.js'; import type { IDcRouterRouteConfig } from '../../dist_ts_interfaces/data/remoteingress.js'; /** The required DNSSEC keying zone when dcrouter currently claims no DNS zone. */ export declare const NO_AUTHORITY_SENTINEL_ZONE = "no-authority.invalid"; /** * Sets up and feeds the embedded authoritative smartdns server: validates the * DNS configuration, generates authoritative/email/DKIM records, applies * proxy-IP replacement, registers record handlers, wires rate-limited query * logging/metrics, and provides the DoH socket handler for SmartProxy routes. */ export declare class DnsServerRuntime { private dcRouterRef; private logWindowSecond; private logWindowCount; private batchCount; private batchTimer; private privateRouteHostnames; private privateRouteTargetIp?; /** Local snapshot of the zone set successfully applied to smartdns. */ private readonly authoritativeZones; private attachedDnsServer?; private cleanupPendingDnsServer?; private cleanupPendingDnsServerPromise?; private lifecycleTail; constructor(dcRouterRef: DcRouter); /** The authority set, from the database and nowhere else. */ private effectiveAuthorityZones; /** * Create the DNS server, start it on UDP, wire metrics/logging, and * register all generated records. */ setup(): Promise; private setupInternal; private retryPendingDnsServerCleanup; /** * Keep retrying until SmartDNS confirms Rust process closure. Taskbuffer * swallows service-stop errors, so returning after a failed termination would * otherwise strand the child and its port bindings with no future retry. */ private ensureDnsServerCleanup; private cleanupDnsServerUntilStopped; private waitForDnsServerCleanupRetry; private enqueueLifecycle; private attachDnsServer; private detachDnsServer; /** * Re-derive the running server's authoritative zone set from the database. * * This is the half of authority that registering handlers cannot express. * smartdns decides the *response kind* from `authoritativeZones` alone: a name * inside a configured zone gets an answer or an authoritative negative, and a * name outside every configured zone gets REFUSED — even when a handler is * registered and answers other qtypes for that exact name. That asymmetry is * the live production defect. `social.io` and `hard.global` are delegated to * us and have handlers, so `A` is answered with the `aa` bit, while `AAAA` and * `SOA` for the same name are REFUSED, and public recursives turn a REFUSED * arm of a dual-stack lookup into SERVFAIL. Verifying a zone therefore has to * update this set, not just register handlers, or the zone stays half-served. * * Idempotent, and safe to call on every authority reconcile. * * @returns the zone set now in effect. */ syncAuthorityZones(reasonArg: string): string[]; /** * Keep an internal-only A-record overlay for exact route hostnames whose * compiled source policy is private or whose only ingress is SmartVPN. * Public routes compile without clientIp restrictions and are never added. * * Every hostname must clear the domain-ownership gate first. The handler uses * SmartDNS' explicit non-authoritative mode, but the server still listens on a * public UDP/TCP 53. Ungated, a private-route definition could publicly hand * out an RFC1918 address for a domain whose real delegation belonged elsewhere. */ syncPrivateRouteOverrides(routesArg: IDcRouterRouteConfig[]): Promise; /** * Drop overlay hostnames whose zone ownership cannot be proven, logging each * rejection at `error` with the reason. Fails closed: if the ownership inputs * cannot be read, no hostname is served rather than all of them. */ private filterOwnedPrivateRouteHostnames; /** Register the split-horizon overlay without claiming DNS authority. */ private registerPrivateRouteHandler; private normalizePrivateRouteHostname; /** Create the DoH handler for cleartext sockets after SmartProxy terminates TLS. */ createSocketHandler(): (socket: plugins.net.Socket) => Promise; /** Flush the pending rate-limited query-log batch and reset logging state. */ flushQueryLogBatch(): void; /** Detach handlers and retain termination ownership until process closure. */ stop(): Promise; private stopInternal; private registerRecords; private parseRecordData; private validateConfiguration; private generateEmailDnsRecords; private loadDkimRecords; private getManagedEmailDomainNames; /** * Nameserver A records (glue) only. * * Generated apex NS records used to be emitted here, one static set per * bootstrap `dnsScopes` entry, registered once at setup and never revisited. * Under database-sourced authority that is wrong twice over: a zone verified * after startup would never get them, and a zone whose authority was revoked * would keep them until an unrelated restart. `DnsManager` owns generated apex * NS for every authoritative zone now — it already did for every zone that was * not in `dnsScopes` — and reconciles them in-process in both directions. * A verified zone with no `DomainDoc` behind it therefore serves no apex NS, * which the `verified-but-unhosted` drift finding reports at `error`. */ private generateAuthoritativeRecords; private extractDomain; private applyProxyIpReplacement; private detectServerPublicIp; }