@serve.zone/dcrouter 18.7.0 → 18.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist_serve/bundle.js +2528 -2206
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +15 -27
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +26 -69
- package/dist_ts/dns/classes.dns-server-runtime.js +138 -98
- package/dist_ts/dns/domain-ownership.d.ts +4 -5
- package/dist_ts/dns/domain-ownership.js +1 -1
- package/dist_ts/dns/manager.dns.d.ts +11 -13
- package/dist_ts/dns/manager.dns.js +156 -74
- package/dist_ts/email/classes.mail-dns-sync.d.ts +0 -1
- package/dist_ts/email/classes.mail-dns-sync.js +10 -38
- package/dist_ts_migrations/index.js +32 -10
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +25 -24
- package/readme.hints.md +98 -84
- package/readme.md +8 -6
- package/third-party-notices.md +13 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +18 -34
- package/ts/dns/classes.dns-server-runtime.ts +156 -107
- package/ts/dns/domain-ownership.ts +4 -5
- package/ts/dns/manager.dns.ts +181 -74
- package/ts/email/classes.mail-dns-sync.ts +9 -35
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -10,16 +10,7 @@ import {
|
|
|
10
10
|
|
|
11
11
|
type TDnsRecordSeed = { name: string; type: string; value: string; ttl?: number; useIngressProxy?: boolean };
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* The zone smartdns is handed when the authority set is empty.
|
|
15
|
-
*
|
|
16
|
-
* smartdns falls back to `[dnssecZone]` whenever `authoritativeZones` is empty,
|
|
17
|
-
* so this value decides what an *unauthoritative* dcrouter would claim. It has
|
|
18
|
-
* to be a name that can never match a real question, or a database that has not
|
|
19
|
-
* been seeded yet would quietly start asserting authority over whatever
|
|
20
|
-
* placeholder we picked. `.invalid` is reserved by RFC 2606 and is guaranteed
|
|
21
|
-
* never to be delegated to anybody.
|
|
22
|
-
*/
|
|
13
|
+
/** The required DNSSEC keying zone when dcrouter currently claims no DNS zone. */
|
|
23
14
|
export const NO_AUTHORITY_SENTINEL_ZONE = 'no-authority.invalid';
|
|
24
15
|
|
|
25
16
|
/**
|
|
@@ -38,28 +29,12 @@ export class DnsServerRuntime {
|
|
|
38
29
|
private privateRouteHostnames = new Set<string>();
|
|
39
30
|
private privateRouteTargetIp?: string;
|
|
40
31
|
|
|
41
|
-
/**
|
|
42
|
-
* The array instance handed to smartdns as `authoritativeZones`.
|
|
43
|
-
*
|
|
44
|
-
* smartdns keeps the options object by reference and re-reads this array on
|
|
45
|
-
* every query, so mutating it in place changes the authority set of a
|
|
46
|
-
* *running* server.
|
|
47
|
-
*
|
|
48
|
-
* **This is a consumer-side shim, and it is sequenced for replacement.**
|
|
49
|
-
* `authoritativeZones` is a public option, but "the array is retained by
|
|
50
|
-
* reference and re-read per query" is storage behaviour, not a documented
|
|
51
|
-
* contract. The proper fix is a `setAuthoritativeZones()` on smartdns' own
|
|
52
|
-
* `DnsServer`; it cannot be written here because that release does not exist
|
|
53
|
-
* yet, and the only alternative available today — re-creating the server to
|
|
54
|
-
* change its zones — drops UDP/TCP 53, which is the 30–60 s public outage
|
|
55
|
-
* this entire path exists to remove.
|
|
56
|
-
*
|
|
57
|
-
* Guarded two ways until the setter ships: `assertLiveAuthorityZones()` logs
|
|
58
|
-
* at `error` if the reference is no longer shared after construction, and a
|
|
59
|
-
* test asserts that identity against the installed smartdns so a release that
|
|
60
|
-
* starts copying the array fails the suite rather than production.
|
|
61
|
-
*/
|
|
32
|
+
/** Local snapshot of the zone set successfully applied to smartdns. */
|
|
62
33
|
private readonly authoritativeZones: string[] = [];
|
|
34
|
+
private attachedDnsServer?: plugins.smartdns.dnsServerMod.DnsServer;
|
|
35
|
+
private cleanupPendingDnsServer?: plugins.smartdns.dnsServerMod.DnsServer;
|
|
36
|
+
private cleanupPendingDnsServerPromise?: Promise<void>;
|
|
37
|
+
private lifecycleTail: Promise<void> = Promise.resolve();
|
|
63
38
|
|
|
64
39
|
constructor(private dcRouterRef: DcRouter) {}
|
|
65
40
|
|
|
@@ -72,7 +47,15 @@ export class DnsServerRuntime {
|
|
|
72
47
|
* Create the DNS server, start it on UDP, wire metrics/logging, and
|
|
73
48
|
* register all generated records.
|
|
74
49
|
*/
|
|
75
|
-
public
|
|
50
|
+
public setup(): Promise<void> {
|
|
51
|
+
return this.enqueueLifecycle(() => this.setupInternal());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private async setupInternal(): Promise<void> {
|
|
55
|
+
await this.retryPendingDnsServerCleanup();
|
|
56
|
+
if (this.dcRouterRef.dnsServer) {
|
|
57
|
+
throw new Error('DNS server setup refused because another server instance is still owned');
|
|
58
|
+
}
|
|
76
59
|
const options = this.dcRouterRef.options;
|
|
77
60
|
if (!options.dnsNsDomains || options.dnsNsDomains.length === 0) {
|
|
78
61
|
throw new Error('dnsNsDomains is required for DNS server setup');
|
|
@@ -154,21 +137,20 @@ export class DnsServerRuntime {
|
|
|
154
137
|
// authority set — sorted, therefore stable across restarts rather than
|
|
155
138
|
// dependent on database insertion order.
|
|
156
139
|
dnssecZone: bootAuthorityZones[0] || NO_AUTHORITY_SENTINEL_ZONE,
|
|
157
|
-
//
|
|
158
|
-
authoritativeZones:
|
|
140
|
+
// SmartDNS copies this boot snapshot; later changes use its live setter.
|
|
141
|
+
authoritativeZones: bootAuthorityZones,
|
|
159
142
|
primaryNameserver: primaryNameserver, // Automatically generates correct SOA records
|
|
160
143
|
// For now, use self-signed cert until we integrate with Let's Encrypt
|
|
161
144
|
httpsKey: '',
|
|
162
145
|
httpsCert: ''
|
|
163
146
|
});
|
|
164
|
-
this.assertLiveAuthorityZones(dnsServer);
|
|
165
147
|
this.dcRouterRef.dnsServer = dnsServer;
|
|
166
148
|
this.registerPrivateRouteHandler(dnsServer);
|
|
167
149
|
|
|
168
|
-
// Start the DNS server. smartdns owns UDP and DNS-over-TCP; dcrouter
|
|
169
|
-
// keeps DNS-over-HTTPS routed through SmartProxy manual HTTPS mode.
|
|
170
|
-
await dnsServer.start();
|
|
171
150
|
try {
|
|
151
|
+
// SmartDNS owns UDP and DNS-over-TCP; dcrouter keeps DNS-over-HTTPS
|
|
152
|
+
// routed through SmartProxy manual HTTPS mode.
|
|
153
|
+
await dnsServer.start();
|
|
172
154
|
logger.log('info', `DNS server started on UDP/TCP ${vmIpAddress}:53`);
|
|
173
155
|
|
|
174
156
|
// Wire DNS query events to MetricsManager and logger with adaptive rate limiting
|
|
@@ -244,26 +226,125 @@ export class DnsServerRuntime {
|
|
|
244
226
|
// dcrouter-hosted domains get registered too.
|
|
245
227
|
await this.attachDnsServer(dnsServer);
|
|
246
228
|
} catch (error) {
|
|
229
|
+
this.detachDnsServer(dnsServer);
|
|
247
230
|
dnsServer.removeAllListeners();
|
|
248
|
-
|
|
231
|
+
let stopError: Error | undefined;
|
|
232
|
+
try {
|
|
233
|
+
await dnsServer.stop();
|
|
234
|
+
} catch (errorDuringStop: unknown) {
|
|
235
|
+
stopError = errorDuringStop instanceof Error
|
|
236
|
+
? errorDuringStop
|
|
237
|
+
: new Error(String(errorDuringStop));
|
|
238
|
+
this.cleanupPendingDnsServer = dnsServer;
|
|
239
|
+
void this.ensureDnsServerCleanup(dnsServer, true);
|
|
249
240
|
logger.log('warn', `Failed to stop DNS server after setup failure: ${stopError.message}`);
|
|
250
|
-
}
|
|
251
|
-
if (this.dcRouterRef.dnsServer === dnsServer) {
|
|
241
|
+
}
|
|
242
|
+
if (!stopError && this.dcRouterRef.dnsServer === dnsServer) {
|
|
252
243
|
this.dcRouterRef.dnsServer = undefined;
|
|
253
244
|
}
|
|
254
245
|
this.flushQueryLogBatch();
|
|
246
|
+
if (stopError) {
|
|
247
|
+
throw new AggregateError(
|
|
248
|
+
[error, stopError],
|
|
249
|
+
'DNS server setup failed and process cleanup remains pending',
|
|
250
|
+
);
|
|
251
|
+
}
|
|
255
252
|
throw error;
|
|
256
253
|
}
|
|
257
254
|
}
|
|
258
255
|
|
|
256
|
+
private async retryPendingDnsServerCleanup(): Promise<void> {
|
|
257
|
+
const pendingServer = this.cleanupPendingDnsServer;
|
|
258
|
+
if (!pendingServer) return;
|
|
259
|
+
await this.ensureDnsServerCleanup(pendingServer);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Keep retrying until SmartDNS confirms Rust process closure. Taskbuffer
|
|
264
|
+
* swallows service-stop errors, so returning after a failed termination would
|
|
265
|
+
* otherwise strand the child and its port bindings with no future retry.
|
|
266
|
+
*/
|
|
267
|
+
private ensureDnsServerCleanup(
|
|
268
|
+
dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer,
|
|
269
|
+
delayFirstAttemptArg = false,
|
|
270
|
+
): Promise<void> {
|
|
271
|
+
if (this.cleanupPendingDnsServerPromise) {
|
|
272
|
+
if (this.cleanupPendingDnsServer !== dnsServerArg) {
|
|
273
|
+
throw new Error('Cannot clean up two DNS server instances concurrently');
|
|
274
|
+
}
|
|
275
|
+
return this.cleanupPendingDnsServerPromise;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.cleanupPendingDnsServer = dnsServerArg;
|
|
279
|
+
let cleanupPromise: Promise<void>;
|
|
280
|
+
cleanupPromise = this.cleanupDnsServerUntilStopped(dnsServerArg, delayFirstAttemptArg)
|
|
281
|
+
.finally(() => {
|
|
282
|
+
if (this.cleanupPendingDnsServerPromise === cleanupPromise) {
|
|
283
|
+
this.cleanupPendingDnsServerPromise = undefined;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
this.cleanupPendingDnsServerPromise = cleanupPromise;
|
|
287
|
+
return cleanupPromise;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async cleanupDnsServerUntilStopped(
|
|
291
|
+
dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer,
|
|
292
|
+
delayFirstAttemptArg: boolean,
|
|
293
|
+
): Promise<void> {
|
|
294
|
+
let failedAttempts = 0;
|
|
295
|
+
if (delayFirstAttemptArg) {
|
|
296
|
+
await this.waitForDnsServerCleanupRetry(1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
while (this.cleanupPendingDnsServer === dnsServerArg) {
|
|
300
|
+
try {
|
|
301
|
+
await dnsServerArg.stop();
|
|
302
|
+
if (this.dcRouterRef.dnsServer === dnsServerArg) {
|
|
303
|
+
this.dcRouterRef.dnsServer = undefined;
|
|
304
|
+
}
|
|
305
|
+
if (this.cleanupPendingDnsServer === dnsServerArg) {
|
|
306
|
+
this.cleanupPendingDnsServer = undefined;
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
} catch (error: unknown) {
|
|
310
|
+
failedAttempts++;
|
|
311
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
312
|
+
logger.log(
|
|
313
|
+
'warn',
|
|
314
|
+
`DNS server process cleanup attempt ${failedAttempts} failed; retrying until closure is confirmed: ${message}`,
|
|
315
|
+
);
|
|
316
|
+
await this.waitForDnsServerCleanupRetry(failedAttempts);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private async waitForDnsServerCleanupRetry(failedAttemptsArg: number): Promise<void> {
|
|
322
|
+
const delayMs = Math.min(250 * (2 ** Math.min(failedAttemptsArg - 1, 5)), 5000);
|
|
323
|
+
await new Promise<void>((resolve) => setTimeout(resolve, delayMs));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private enqueueLifecycle(operationArg: () => Promise<void>): Promise<void> {
|
|
327
|
+
const operation = this.lifecycleTail.then(operationArg, operationArg);
|
|
328
|
+
this.lifecycleTail = operation.then(() => undefined, () => undefined);
|
|
329
|
+
return operation;
|
|
330
|
+
}
|
|
331
|
+
|
|
259
332
|
private async attachDnsServer(
|
|
260
333
|
dnsServer: plugins.smartdns.dnsServerMod.DnsServer,
|
|
261
334
|
): Promise<void> {
|
|
262
335
|
if (!this.dcRouterRef.dnsManager) return;
|
|
263
336
|
await this.dcRouterRef.dnsManager.attachDnsServer(dnsServer);
|
|
337
|
+
this.attachedDnsServer = dnsServer;
|
|
264
338
|
this.dcRouterRef.mailDnsSync?.requestSync('DNS server attached');
|
|
265
339
|
}
|
|
266
340
|
|
|
341
|
+
private detachDnsServer(dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer): void {
|
|
342
|
+
this.dcRouterRef.dnsManager?.detachDnsServer(dnsServerArg);
|
|
343
|
+
if (this.attachedDnsServer === dnsServerArg) {
|
|
344
|
+
this.attachedDnsServer = undefined;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
267
348
|
/**
|
|
268
349
|
* Re-derive the running server's authoritative zone set from the database.
|
|
269
350
|
*
|
|
@@ -286,42 +367,18 @@ export class DnsServerRuntime {
|
|
|
286
367
|
const nextZones = this.effectiveAuthorityZones();
|
|
287
368
|
const changed = nextZones.length !== this.authoritativeZones.length
|
|
288
369
|
|| nextZones.some((zone, index) => zone !== this.authoritativeZones[index]);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (changed) {
|
|
292
|
-
logger.log(
|
|
293
|
-
'info',
|
|
294
|
-
`DNS authoritative zone set is now [${nextZones.join(', ') || 'empty'}] (${reasonArg})`,
|
|
295
|
-
{ zone: 'dns' },
|
|
296
|
-
);
|
|
370
|
+
if (!changed) {
|
|
371
|
+
return nextZones;
|
|
297
372
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Prove smartdns kept our array rather than copying it.
|
|
303
|
-
*
|
|
304
|
-
* If this ever fails, authority silently freezes at its boot-time value and
|
|
305
|
-
* every runtime verification stops taking effect until a restart — a failure
|
|
306
|
-
* that is invisible from the outside because the server keeps answering
|
|
307
|
-
* correctly for the zones it started with. Logged rather than thrown: a frozen
|
|
308
|
-
* authority set is a degradation, and taking DNS down entirely over it would
|
|
309
|
-
* be worse than the fault it reports.
|
|
310
|
-
*/
|
|
311
|
-
private assertLiveAuthorityZones(
|
|
312
|
-
dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer,
|
|
313
|
-
): void {
|
|
314
|
-
const heldZones = (dnsServerArg as unknown as {
|
|
315
|
-
options?: { authoritativeZones?: string[] };
|
|
316
|
-
}).options?.authoritativeZones;
|
|
317
|
-
if (heldZones === this.authoritativeZones) return;
|
|
373
|
+
// Commit the local snapshot only after the live server accepts the update.
|
|
374
|
+
this.dcRouterRef.dnsServer?.setAuthoritativeZones(nextZones);
|
|
375
|
+
this.authoritativeZones.splice(0, this.authoritativeZones.length, ...nextZones);
|
|
318
376
|
logger.log(
|
|
319
|
-
'
|
|
320
|
-
|
|
321
|
-
+ 'reconciled without restarting the DNS server. Zones verified at runtime will register handlers but '
|
|
322
|
-
+ 'keep answering REFUSED for every qtype they do not handle until dcrouter is restarted.',
|
|
377
|
+
'info',
|
|
378
|
+
`DNS authoritative zone set is now [${nextZones.join(', ') || 'empty'}] (${reasonArg})`,
|
|
323
379
|
{ zone: 'dns' },
|
|
324
380
|
);
|
|
381
|
+
return nextZones;
|
|
325
382
|
}
|
|
326
383
|
|
|
327
384
|
/**
|
|
@@ -329,12 +386,10 @@ export class DnsServerRuntime {
|
|
|
329
386
|
* compiled source policy is private or whose only ingress is SmartVPN.
|
|
330
387
|
* Public routes compile without clientIp restrictions and are never added.
|
|
331
388
|
*
|
|
332
|
-
* Every hostname must clear the domain-ownership gate first.
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* out an RFC1918 address, with the `aa` flag, for a domain whose real
|
|
337
|
-
* delegation belonged to somebody else.
|
|
389
|
+
* Every hostname must clear the domain-ownership gate first. The handler uses
|
|
390
|
+
* SmartDNS' explicit non-authoritative mode, but the server still listens on a
|
|
391
|
+
* public UDP/TCP 53. Ungated, a private-route definition could publicly hand
|
|
392
|
+
* out an RFC1918 address for a domain whose real delegation belonged elsewhere.
|
|
338
393
|
*/
|
|
339
394
|
public async syncPrivateRouteOverrides(routesArg: IDcRouterRouteConfig[]): Promise<void> {
|
|
340
395
|
const candidateHostnames = new Set<string>();
|
|
@@ -425,23 +480,7 @@ export class DnsServerRuntime {
|
|
|
425
480
|
return ownedHostnames;
|
|
426
481
|
}
|
|
427
482
|
|
|
428
|
-
/**
|
|
429
|
-
* SEQUENCED BEHIND A SMARTDNS RELEASE — this registration must become
|
|
430
|
-
* `{ authority: 'non-authoritative', owner: 'private-route-overlay' }`.
|
|
431
|
-
*
|
|
432
|
-
* The overlay is a split-horizon answer by intent: it hands out an internal
|
|
433
|
-
* address for a hostname whose zone we own, and it deliberately matches `*`
|
|
434
|
-
* so it can cover any owned hostname. Under the hardened smartdns authority
|
|
435
|
-
* model, a handler left at the default `authoritative` mode is *suppressed*
|
|
436
|
-
* for any name outside every configured `authoritativeZones` entry. Overlay
|
|
437
|
-
* hostnames proven by `provider-zone` are exactly that — owned, but not in
|
|
438
|
-
* the delegation-verified set — so on that smartdns they would stop being
|
|
439
|
-
* answered entirely, silently.
|
|
440
|
-
*
|
|
441
|
-
* The options parameter does not exist in the installed smartdns (7.12.1) and
|
|
442
|
-
* the change adding it is unreleased, so this cannot be written yet. It must
|
|
443
|
-
* land in the same change that takes the smartdns bump.
|
|
444
|
-
*/
|
|
483
|
+
/** Register the split-horizon overlay without claiming DNS authority. */
|
|
445
484
|
private registerPrivateRouteHandler(
|
|
446
485
|
dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer,
|
|
447
486
|
): void {
|
|
@@ -462,7 +501,7 @@ export class DnsServerRuntime {
|
|
|
462
501
|
ttl: 60,
|
|
463
502
|
data: this.privateRouteTargetIp,
|
|
464
503
|
};
|
|
465
|
-
});
|
|
504
|
+
}, { authority: 'non-authoritative', owner: 'private-route-overlay' });
|
|
466
505
|
}
|
|
467
506
|
|
|
468
507
|
private normalizePrivateRouteHostname(hostnameArg: string): string | undefined {
|
|
@@ -475,9 +514,7 @@ export class DnsServerRuntime {
|
|
|
475
514
|
return hostname;
|
|
476
515
|
}
|
|
477
516
|
|
|
478
|
-
/**
|
|
479
|
-
* Create the DoH socket handler SmartProxy routes hand TLS sockets to.
|
|
480
|
-
*/
|
|
517
|
+
/** Create the DoH handler for cleartext sockets after SmartProxy terminates TLS. */
|
|
481
518
|
public createSocketHandler(): (socket: plugins.net.Socket) => Promise<void> {
|
|
482
519
|
return async (socket: plugins.net.Socket) => {
|
|
483
520
|
if (!this.dcRouterRef.dnsServer) {
|
|
@@ -497,9 +534,7 @@ export class DnsServerRuntime {
|
|
|
497
534
|
logger.log('debug', 'DNS socket handler: passing socket to DnsServer');
|
|
498
535
|
|
|
499
536
|
try {
|
|
500
|
-
|
|
501
|
-
// This handles HTTP/2, DoH protocol, etc.
|
|
502
|
-
await (this.dcRouterRef.dnsServer as any).handleHttpsSocket(socket);
|
|
537
|
+
this.dcRouterRef.dnsServer.handleHttpSocket(socket);
|
|
503
538
|
} catch (error: unknown) {
|
|
504
539
|
logger.log('error', `DNS socket handler error: ${(error as Error).message}`);
|
|
505
540
|
if (!socket.destroyed) {
|
|
@@ -523,9 +558,23 @@ export class DnsServerRuntime {
|
|
|
523
558
|
}
|
|
524
559
|
}
|
|
525
560
|
|
|
526
|
-
/**
|
|
527
|
-
public
|
|
561
|
+
/** Detach handlers and retain termination ownership until process closure. */
|
|
562
|
+
public stop(): Promise<void> {
|
|
563
|
+
return this.enqueueLifecycle(() => this.stopInternal());
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private async stopInternal(): Promise<void> {
|
|
528
567
|
this.flushQueryLogBatch();
|
|
568
|
+
const ownedServers = new Set<plugins.smartdns.dnsServerMod.DnsServer>();
|
|
569
|
+
if (this.attachedDnsServer) ownedServers.add(this.attachedDnsServer);
|
|
570
|
+
if (this.cleanupPendingDnsServer) ownedServers.add(this.cleanupPendingDnsServer);
|
|
571
|
+
if (this.dcRouterRef.dnsServer) ownedServers.add(this.dcRouterRef.dnsServer);
|
|
572
|
+
|
|
573
|
+
for (const dnsServer of ownedServers) {
|
|
574
|
+
this.detachDnsServer(dnsServer);
|
|
575
|
+
dnsServer.removeAllListeners();
|
|
576
|
+
await this.ensureDnsServerCleanup(dnsServer);
|
|
577
|
+
}
|
|
529
578
|
}
|
|
530
579
|
|
|
531
580
|
private registerRecords(records: TDnsRecordSeed[]): void {
|
|
@@ -560,8 +609,8 @@ export class DnsServerRuntime {
|
|
|
560
609
|
case 'A':
|
|
561
610
|
return value; // IP address as string
|
|
562
611
|
case 'MX':
|
|
563
|
-
const [
|
|
564
|
-
return {
|
|
612
|
+
const [preference, exchange] = value.split(' ');
|
|
613
|
+
return { preference: parseInt(preference, 10), exchange };
|
|
565
614
|
case 'TXT':
|
|
566
615
|
return value;
|
|
567
616
|
case 'NS':
|
|
@@ -20,11 +20,10 @@ import type { TDomainSource } from '../../ts_interfaces/data/domain.js';
|
|
|
20
20
|
* provisioning budget was consumed against a cause no retry can fix and the
|
|
21
21
|
* certificates silently expired.
|
|
22
22
|
* - `DomainDoc`s created through the ops API set `authoritative = true`
|
|
23
|
-
* unconditionally, and
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* third parties.
|
|
23
|
+
* unconditionally, and older embedded SmartDNS releases treated a handler
|
|
24
|
+
* answer as authoritative regardless of configured zones. dcrouter therefore
|
|
25
|
+
* served apex NS records and an RFC1918 A record publicly for zones whose
|
|
26
|
+
* real delegation belonged to third parties.
|
|
28
27
|
*
|
|
29
28
|
* There are exactly two proofs available in-process, neither of which an ops-API
|
|
30
29
|
* caller can forge:
|