@push.rocks/smartdns 8.1.0 → 8.2.1
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/dist_rust/rustdns-client_linux_amd64 +0 -0
- package/dist_rust/rustdns-client_linux_amd64.tsrust-build.json +14 -0
- package/dist_rust/rustdns-client_linux_arm64 +0 -0
- package/dist_rust/rustdns-client_linux_arm64.tsrust-build.json +14 -0
- package/dist_rust/rustdns_linux_amd64 +0 -0
- package/dist_rust/rustdns_linux_amd64.tsrust-build.json +14 -0
- package/dist_rust/rustdns_linux_arm64 +0 -0
- package/dist_rust/rustdns_linux_arm64.tsrust-build.json +14 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts_server/classes.dnsserver.d.ts +44 -2
- package/dist_ts_server/classes.dnsserver.js +573 -42
- package/dist_ts_server/classes.rustdnsbridge.d.ts +4 -0
- package/dist_ts_server/classes.rustdnsbridge.js +10 -1
- package/dist_ts_server/plugins.d.ts +4 -1
- package/dist_ts_server/plugins.js +5 -2
- package/package.json +6 -6
- package/readme.hints.md +41 -21
- package/readme.md +58 -4
- package/ts/00_commitinfo_data.ts +1 -1
|
@@ -130,6 +130,10 @@ export declare class RustDnsBridge extends plugins.events.EventEmitter {
|
|
|
130
130
|
* Kill the Rust process.
|
|
131
131
|
*/
|
|
132
132
|
kill(): void;
|
|
133
|
+
/**
|
|
134
|
+
* Terminate the Rust process and resolve only after closure is confirmed.
|
|
135
|
+
*/
|
|
136
|
+
terminate(): Promise<void>;
|
|
133
137
|
/**
|
|
134
138
|
* Whether the bridge is running.
|
|
135
139
|
*/
|
|
@@ -23,6 +23,9 @@ export class RustDnsBridge extends plugins.events.EventEmitter {
|
|
|
23
23
|
cliArgs: ['--management'],
|
|
24
24
|
requestTimeoutMs: 30_000,
|
|
25
25
|
readyTimeoutMs: 10_000,
|
|
26
|
+
maxPendingRequestsByMethod: {
|
|
27
|
+
processPacket: 256,
|
|
28
|
+
},
|
|
26
29
|
localPaths,
|
|
27
30
|
searchSystemPath: false,
|
|
28
31
|
});
|
|
@@ -107,6 +110,12 @@ export class RustDnsBridge extends plugins.events.EventEmitter {
|
|
|
107
110
|
kill() {
|
|
108
111
|
this.bridge.kill();
|
|
109
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Terminate the Rust process and resolve only after closure is confirmed.
|
|
115
|
+
*/
|
|
116
|
+
async terminate() {
|
|
117
|
+
await this.bridge.terminate();
|
|
118
|
+
}
|
|
110
119
|
/**
|
|
111
120
|
* Whether the bridge is running.
|
|
112
121
|
*/
|
|
@@ -137,4 +146,4 @@ function getPlatformSuffix() {
|
|
|
137
146
|
}
|
|
138
147
|
return null;
|
|
139
148
|
}
|
|
140
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
149
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydXN0ZG5zYnJpZGdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfc2VydmVyL2NsYXNzZXMucnVzdGRuc2JyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQTBGeEM7O0dBRUc7QUFDSCxNQUFNLE9BQU8sYUFBYyxTQUFRLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWTtJQUc1RDtRQUNFLEtBQUssRUFBRSxDQUFDO1FBRVIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQ3JDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQ3ZELElBQUksQ0FDTCxDQUFDO1FBRUYsMkVBQTJFO1FBQzNFLE1BQU0sY0FBYyxHQUFHLGlCQUFpQixFQUFFLENBQUM7UUFDM0MsTUFBTSxVQUFVLEdBQWEsRUFBRSxDQUFDO1FBRWhDLDBFQUEwRTtRQUMxRSxJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ25CLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxXQUFXLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMzRixDQUFDO1FBQ0QsMkNBQTJDO1FBQzNDLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQ3ZFLHdCQUF3QjtRQUN4QixVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO1FB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
|
|
@@ -2,9 +2,12 @@ import crypto from 'crypto';
|
|
|
2
2
|
import dgram from 'dgram';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
4
|
import fs from 'fs';
|
|
5
|
+
import * as http from 'node:http';
|
|
6
|
+
import * as http2 from 'node:http2';
|
|
5
7
|
import * as net from 'net';
|
|
6
8
|
import * as path from 'path';
|
|
7
|
-
|
|
9
|
+
import * as stream from 'node:stream';
|
|
10
|
+
export { crypto, dgram, fs, http, http2, net, path, stream, };
|
|
8
11
|
export declare const events: {
|
|
9
12
|
EventEmitter: typeof EventEmitter;
|
|
10
13
|
};
|
|
@@ -3,9 +3,12 @@ import crypto from 'crypto';
|
|
|
3
3
|
import dgram from 'dgram';
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import fs from 'fs';
|
|
6
|
+
import * as http from 'node:http';
|
|
7
|
+
import * as http2 from 'node:http2';
|
|
6
8
|
import * as net from 'net';
|
|
7
9
|
import * as path from 'path';
|
|
8
|
-
|
|
10
|
+
import * as stream from 'node:stream';
|
|
11
|
+
export { crypto, dgram, fs, http, http2, net, path, stream, };
|
|
9
12
|
export const events = { EventEmitter };
|
|
10
13
|
// @push.rocks scope
|
|
11
14
|
import * as smartrust from '@push.rocks/smartrust';
|
|
@@ -13,4 +16,4 @@ export { smartrust, };
|
|
|
13
16
|
// third party
|
|
14
17
|
import * as minimatch from 'minimatch';
|
|
15
18
|
export { minimatch, };
|
|
16
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3NlcnZlci9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWM7QUFDZCxPQUFPLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDNUIsT0FBTyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBQzFCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDdEMsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxLQUFLLE1BQU0sWUFBWSxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxNQUFNLE1BQU0sYUFBYSxDQUFDO0FBRXRDLE9BQU8sRUFDTCxNQUFNLEVBQ04sS0FBSyxFQUNMLEVBQUUsRUFDRixJQUFJLEVBQ0osS0FBSyxFQUNMLEdBQUcsRUFDSCxJQUFJLEVBQ0osTUFBTSxHQUNQLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxNQUFNLEdBQUcsRUFBRSxZQUFZLEVBQUUsQ0FBQztBQUV2QyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUVuRCxPQUFPLEVBQ0wsU0FBUyxHQUNWLENBQUE7QUFFRCxjQUFjO0FBQ2QsT0FBTyxLQUFLLFNBQVMsTUFBTSxXQUFXLENBQUM7QUFFdkMsT0FBTyxFQUNMLFNBQVMsR0FDVixDQUFBIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartdns",
|
|
3
|
-
"version": "8.1
|
|
3
|
+
"version": "8.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A robust TypeScript library providing advanced DNS management and resolution capabilities including support for DNSSEC, custom DNS servers, and integration with various DNS providers.",
|
|
6
6
|
"exports": {
|
|
@@ -40,20 +40,20 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@push.rocks/smartdelay": "^3.0.1",
|
|
42
42
|
"@push.rocks/smartenv": "^6.0.0",
|
|
43
|
-
"@push.rocks/smartpromise": "^4.2.
|
|
44
|
-
"@push.rocks/smartrust": "^1.
|
|
43
|
+
"@push.rocks/smartpromise": "^4.2.5",
|
|
44
|
+
"@push.rocks/smartrust": "^1.8.0",
|
|
45
45
|
"@tsclass/tsclass": "^9.5.1",
|
|
46
46
|
"acme-client": "^5.4.0",
|
|
47
|
-
"minimatch": "^10.2.
|
|
47
|
+
"minimatch": "^10.2.6"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@git.zone/tsbuild": "^4.4.2",
|
|
51
51
|
"@git.zone/tsrun": "^2.0.6",
|
|
52
|
-
"@git.zone/tsrust": "^1.
|
|
52
|
+
"@git.zone/tsrust": "^1.10.0",
|
|
53
53
|
"@git.zone/tstest": "^4.0.0",
|
|
54
54
|
"@types/dns-packet": "^5.6.5",
|
|
55
55
|
"@types/lodash.clonedeep": "^4.5.9",
|
|
56
|
-
"@types/node": "^26.
|
|
56
|
+
"@types/node": "^26.2.0",
|
|
57
57
|
"dns-packet": "^5.6.1"
|
|
58
58
|
},
|
|
59
59
|
"files": [
|
package/readme.hints.md
CHANGED
|
@@ -11,8 +11,12 @@ The smartdns library is structured into three main modules:
|
|
|
11
11
|
|
|
12
12
|
## Rust Backend Architecture (v8.0+)
|
|
13
13
|
|
|
14
|
-
The DNS server's
|
|
15
|
-
|
|
14
|
+
The DNS server's default UDP, TCP, and HTTPS network I/O, packet
|
|
15
|
+
parsing/encoding, and DNSSEC signing run in Rust. TypeScript retains handler
|
|
16
|
+
registration, ACME orchestration, and the public API. `handleHttpSocket()` is
|
|
17
|
+
the deliberate manual-transport exception: after another component terminates
|
|
18
|
+
TLS, TypeScript bounds and parses HTTP/1.1 or prior-knowledge HTTP/2, then sends
|
|
19
|
+
the contained DNS packet to Rust for resolution and response construction.
|
|
16
20
|
|
|
17
21
|
### Rust Crate Structure
|
|
18
22
|
- `rustdns` - Main binary with IPC management loop (`--management` flag)
|
|
@@ -23,9 +27,9 @@ TypeScript retains handler registration, ACME orchestration, and the public API.
|
|
|
23
27
|
### IPC Flow
|
|
24
28
|
```
|
|
25
29
|
DNS Query -> Rust (UDP/HTTPS) -> Parse packet
|
|
26
|
-
-> Try local resolution (localhost
|
|
30
|
+
-> Try local resolution (localhost)
|
|
27
31
|
-> If handler needed: emit "dnsQuery" event to TypeScript
|
|
28
|
-
-> TypeScript runs minimatch handlers, sends "dnsQueryResult" back
|
|
32
|
+
-> TypeScript runs minimatch handlers and authorizes DNSKEY, sends "dnsQueryResult" back
|
|
29
33
|
-> Rust builds response, signs DNSSEC if requested, sends packet
|
|
30
34
|
```
|
|
31
35
|
|
|
@@ -147,9 +151,14 @@ The test suite demonstrates:
|
|
|
147
151
|
|
|
148
152
|
Authority comes from **configuration**, never from the presence of a handler.
|
|
149
153
|
|
|
150
|
-
- `authoritativeZones`
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
- `authoritativeZones` is the single source of truth for the `aa` bit. Omission
|
|
155
|
+
falls back to `[dnssecZone]`; an explicit empty array means no authority.
|
|
156
|
+
`setAuthoritativeZones()` validates and copies a replacement set synchronously,
|
|
157
|
+
so listeners stay up while subsequent queries see the new authority.
|
|
158
|
+
- DNSKEY queries for `dnssecZone` cross the TypeScript query callback before
|
|
159
|
+
Rust synthesizes the signed RRset. This keeps built-in DNSSEC answers inside
|
|
160
|
+
the same live authority boundary; an empty set cannot leak an authoritative
|
|
161
|
+
DNSKEY answer from the fixed Rust configuration.
|
|
153
162
|
- `registerHandler(pattern, types, handler, options?)` takes
|
|
154
163
|
`options.authority`:
|
|
155
164
|
- `'authoritative'` (default) — the handler only contributes for names inside
|
|
@@ -231,8 +240,12 @@ authoritative by definition, not by delegation.
|
|
|
231
240
|
compress names, so record sizes are additive (`DnsPacket::encoded_len`).
|
|
232
241
|
`processRawDnsPacketAsync(packet, 'udp')` declares the same UDP bound for the
|
|
233
242
|
manual path (`handleUdpMessage` passes it automatically); without the hint no
|
|
234
|
-
size bound is applied, which is correct for TCP and DoH framing.
|
|
235
|
-
|
|
243
|
+
size bound is applied, which is correct for TCP and DoH framing. Manual packet
|
|
244
|
+
work runs through a bounded task set so the management loop remains available
|
|
245
|
+
for handler callbacks and their results. The TypeScript bridge mirrors the
|
|
246
|
+
Rust task bound by admitting at most 256 pending `processPacket` requests;
|
|
247
|
+
excess requests fail before serialization with
|
|
248
|
+
`ERR_RUST_BRIDGE_REQUEST_LIMIT`, while control methods remain unbounded.
|
|
236
249
|
- **RA** is cleared on every path — this server never recurses.
|
|
237
250
|
- **RDATA validation** fails closed: a handler returning an unencodable value
|
|
238
251
|
(for example a non-IPv4 string for an A record) produces SERVFAIL and an error
|
|
@@ -247,6 +260,25 @@ authoritative by definition, not by delegation.
|
|
|
247
260
|
carrying the handler `id`, `domainPattern` and `owner`. Wire this up: the
|
|
248
261
|
original incident was invisible for weeks because nothing surfaced it.
|
|
249
262
|
|
|
263
|
+
## Server Lifecycle
|
|
264
|
+
|
|
265
|
+
- `start()` and `stop()` share a serialized lifecycle queue. Each caller keeps
|
|
266
|
+
its own operation result, and a rejection does not poison later operations.
|
|
267
|
+
- Repeated `start()` calls are idempotent at the `DnsServer` facade. Rust also
|
|
268
|
+
rejects duplicate active `start` commands, so low-level bridge callers cannot
|
|
269
|
+
replace listeners while detached HTTPS connection work still exists.
|
|
270
|
+
- `stop()` awaits confirmed Rust child closure before clearing ownership and
|
|
271
|
+
legacy server markers. A failed termination retains them so cleanup can be
|
|
272
|
+
retried safely.
|
|
273
|
+
- If an operation and confirmed cleanup both fail, both errors are preserved in
|
|
274
|
+
an `AggregateError`. A failed stop command is suppressed only after process
|
|
275
|
+
closure has been confirmed.
|
|
276
|
+
- A server may be started again after a confirmed stop; smartrust activates a
|
|
277
|
+
fresh process generation on the same bridge.
|
|
278
|
+
- Packet IPC is admitted only while no lifecycle operation is queued and the
|
|
279
|
+
facade state, ownership marker, and bridge liveness all agree. Certificate
|
|
280
|
+
updates use the same queue so they cannot race startup or shutdown.
|
|
281
|
+
|
|
250
282
|
## Known Limitations
|
|
251
283
|
|
|
252
284
|
1. **Handler Deduplication**: If the same handler is registered multiple times, it will contribute duplicate records (this may be desired behavior for some use cases)
|
|
@@ -254,18 +286,6 @@ authoritative by definition, not by delegation.
|
|
|
254
286
|
3. **SOA serial does not track zone changes** — it is a process-lifetime
|
|
255
287
|
constant, not a change counter. Secondaries driven by AXFR/IXFR are not
|
|
256
288
|
supported.
|
|
257
|
-
4. **Manual-path (`processPacket`) queries deadlock on handler resolution**
|
|
258
|
-
— pre-existing, not introduced by the v8 hardening. The IPC select loop in
|
|
259
|
-
`rust/crates/rustdns/src/management.rs` awaits `handle_request` inline, so
|
|
260
|
-
while `handle_process_packet` waits for the TypeScript `dnsQuery` callback
|
|
261
|
-
the loop can neither emit the query event nor read its result; every
|
|
262
|
-
handler-backed query through `processRawDnsPacketAsync` /
|
|
263
|
-
`handleUdpMessage` times out to SERVFAIL after 10 s. The built-in UDP, TCP
|
|
264
|
-
and DoH listeners are unaffected (they resolve outside the IPC request
|
|
265
|
-
loop). The `transport: 'udp'` truncation contract on this path is wired
|
|
266
|
-
and unit-tested but cannot be observed end-to-end until the loop spawns
|
|
267
|
-
`processPacket` handling instead of awaiting it inline. No test exercises
|
|
268
|
-
`processRawDnsPacketAsync`; fixing this needs its own release.
|
|
269
289
|
## Static Rust Binaries
|
|
270
290
|
|
|
271
291
|
- The Rust binaries in `dist_rust/` are statically linked (static-pie) via `"static": true` in the `@git.zone/tsrust` block of `.smartconfig.json` (tsrust >= 1.4.1). They run on both glibc (Debian/Ubuntu) and musl (Alpine) systems.
|
package/readme.md
CHANGED
|
@@ -270,7 +270,7 @@ interface IDnsServerOptions {
|
|
|
270
270
|
httpsKey: string; // PEM private key (path or content)
|
|
271
271
|
httpsCert: string; // PEM certificate (path or content)
|
|
272
272
|
dnssecZone: string; // Zone for DNSSEC signing
|
|
273
|
-
authoritativeZones?: string[]; //
|
|
273
|
+
authoritativeZones?: string[]; // Omitted defaults to [dnssecZone]; [] claims no zone
|
|
274
274
|
primaryNameserver?: string; // SOA mname field (default: 'ns1.{dnssecZone}')
|
|
275
275
|
udpBindInterface?: string; // IP to bind UDP (default: '0.0.0.0')
|
|
276
276
|
tcpBindInterface?: string; // IP to bind TCP (defaults to udpBindInterface)
|
|
@@ -316,12 +316,32 @@ await server.start();
|
|
|
316
316
|
// DNS Server started (UDP: 0.0.0.0:5333, TCP: 0.0.0.0:5333, HTTPS: 0.0.0.0:8443)
|
|
317
317
|
```
|
|
318
318
|
|
|
319
|
+
Calling `start()` again while this `DnsServer` is running is an idempotent
|
|
320
|
+
no-op. After a confirmed `stop()`, the same instance can be started again with
|
|
321
|
+
a fresh Rust process.
|
|
322
|
+
|
|
319
323
|
### Handler System 🎯
|
|
320
324
|
|
|
321
325
|
Handlers use **glob patterns** (via `minimatch`) to match incoming query names. Multiple handlers can contribute records to the same response.
|
|
322
326
|
|
|
323
327
|
Registering a handler does **not** make the server authoritative for the matched names. Authority comes solely from `authoritativeZones` (falling back to `dnssecZone`): list every zone you serve there, or queries for it are withheld. A default-authority handler whose match falls outside every configured zone is suppressed and reported on the `refused` event; register with `{ authority: 'non-authoritative' }` to deliberately serve out-of-zone names without the `aa` bit or DNSSEC signatures.
|
|
324
328
|
|
|
329
|
+
Replace the live authority set without restarting UDP, TCP, or HTTPS listeners:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
server.setAuthoritativeZones(['example.com', 'example.net']);
|
|
333
|
+
server.setAuthoritativeZones([]); // explicitly claim no zone
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
The constructor copies its supplied array, and `setAuthoritativeZones()` validates
|
|
337
|
+
and copies replacements. Omitting `authoritativeZones` from the constructor
|
|
338
|
+
retains the backward-compatible `[dnssecZone]` default; passing or setting an
|
|
339
|
+
empty array does not fall back. DNSKEY answers for
|
|
340
|
+
`dnssecZone` pass through the same live authority decision, so removing the last
|
|
341
|
+
zone also removes the last DNSSEC authority claim. RFC 6761 `localhost` handling
|
|
342
|
+
is the deliberate exception: while enabled, built-in localhost A/AAAA answers
|
|
343
|
+
remain authoritative even when the configured zone set is empty.
|
|
344
|
+
|
|
325
345
|
```typescript
|
|
326
346
|
// Exact domain
|
|
327
347
|
server.registerHandler('example.com', ['A'], handler);
|
|
@@ -382,7 +402,7 @@ DNSSEC is enabled automatically when you set the `dnssecZone` option. The Rust b
|
|
|
382
402
|
|
|
383
403
|
- **Key generation** — ECDSA P-256 (algorithm 13) by default
|
|
384
404
|
- **DNSKEY / DS record** generation
|
|
385
|
-
- **RRSIG signing** for
|
|
405
|
+
- **RRSIG signing** for authoritative answers when the query requests DNSSEC
|
|
386
406
|
- **NSEC records** for authenticated denial of existence
|
|
387
407
|
|
|
388
408
|
```typescript
|
|
@@ -428,7 +448,7 @@ const server = new DnsServer({
|
|
|
428
448
|
|
|
429
449
|
### Let's Encrypt Integration 🔒
|
|
430
450
|
|
|
431
|
-
Built-in ACME DNS-01 challenge support for automatic SSL certificates. Challenge TXT handlers are registered at default authority, so DNS-01 validation only completes for domains inside `authoritativeZones
|
|
451
|
+
Built-in ACME DNS-01 challenge support for automatic SSL certificates. Challenge TXT handlers are registered at default authority, so DNS-01 validation only completes for domains inside `authoritativeZones`, or inside `dnssecZone` when `authoritativeZones` is omitted:
|
|
432
452
|
|
|
433
453
|
```typescript
|
|
434
454
|
const server = new DnsServer({
|
|
@@ -520,6 +540,29 @@ const server = new DnsServer({
|
|
|
520
540
|
await server.start(); // No UDP, TCP, or HTTPS listener binds automatically
|
|
521
541
|
```
|
|
522
542
|
|
|
543
|
+
After `start()` resolves, an upstream proxy may terminate TLS and hand the
|
|
544
|
+
resulting cleartext HTTP socket to SmartDNS. The handler detects HTTP/1.1 and
|
|
545
|
+
prior-knowledge HTTP/2 (not h2c Upgrade) and accepts RFC 8484 requests on
|
|
546
|
+
`/dns-query`. `/resolve` remains an alias for the already-shipped dcrouter route:
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
const handleTlsTerminatedSocket = (socket: import('node:net').Socket) => {
|
|
550
|
+
server.handleHttpSocket(socket);
|
|
551
|
+
};
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
GET requests require an unpadded base64url `dns` query parameter. POST requests
|
|
555
|
+
require `Content-Type: application/dns-message`. DNS messages are limited to
|
|
556
|
+
65,535 bytes, headers to 16 KiB, HTTP/2 sessions to 100 concurrent streams, and
|
|
557
|
+
header/request/session lifetimes are bounded. Successful responses use
|
|
558
|
+
`application/dns-message`; malformed or unsupported requests receive an explicit
|
|
559
|
+
4xx response, internal handler failures receive 500, and unavailable DNS
|
|
560
|
+
processing receives 503. Every error response uses `text/plain; charset=utf-8`.
|
|
561
|
+
|
|
562
|
+
`handleHttpSocket()` must only receive a TLS-terminated cleartext stream. The
|
|
563
|
+
legacy `handleHttpsSocket()` does not terminate encrypted TLS in Rust mode and
|
|
564
|
+
closes the socket.
|
|
565
|
+
|
|
523
566
|
Process individual DNS packets directly:
|
|
524
567
|
|
|
525
568
|
```typescript
|
|
@@ -533,6 +576,12 @@ const udpResponse = await server.processRawDnsPacketAsync(packetBuffer, 'udp');
|
|
|
533
576
|
|
|
534
577
|
Manual TCP callers must handle DNS-over-TCP's two-byte length prefix themselves and pass only the raw DNS packet payload to `processRawDnsPacketAsync()`.
|
|
535
578
|
|
|
579
|
+
At most 256 `processRawDnsPacketAsync()` calls may be pending at once. Additional
|
|
580
|
+
calls reject before transport writes with a `RustBridgeRequestError` whose code
|
|
581
|
+
is `ERR_RUST_BRIDGE_REQUEST_LIMIT`; control and callback commands remain
|
|
582
|
+
available while packet work is saturated. New packet calls are also rejected
|
|
583
|
+
while `start()`, `stop()`, or a certificate update is queued or running.
|
|
584
|
+
|
|
536
585
|
#### Load Balancing Example
|
|
537
586
|
|
|
538
587
|
```typescript
|
|
@@ -560,7 +609,12 @@ for (let i = 0; i < numCPUs; i++) {
|
|
|
560
609
|
await server.stop();
|
|
561
610
|
```
|
|
562
611
|
|
|
563
|
-
|
|
612
|
+
`stop()` is serialized with `start()` and resolves only after the Rust process
|
|
613
|
+
has closed and its bound sockets are released. If confirmed termination fails,
|
|
614
|
+
the call rejects and retains ownership so a later `stop()` can retry cleanup.
|
|
615
|
+
It also destroys every HTTP/1.1 socket and HTTP/2 replay stream handed to
|
|
616
|
+
`handleHttpSocket()`; the upstream proxy/listener remains caller-owned and must
|
|
617
|
+
be stopped separately.
|
|
564
618
|
|
|
565
619
|
---
|
|
566
620
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartdns',
|
|
6
|
-
version: '8.1
|
|
6
|
+
version: '8.2.1',
|
|
7
7
|
description: 'A robust TypeScript library providing advanced DNS management and resolution capabilities including support for DNSSEC, custom DNS servers, and integration with various DNS providers.'
|
|
8
8
|
}
|