@push.rocks/smartdns 7.6.1 → 7.8.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.
Files changed (33) hide show
  1. package/dist_rust/rustdns-client_linux_amd64 +0 -0
  2. package/dist_rust/rustdns-client_linux_arm64 +0 -0
  3. package/dist_rust/rustdns_linux_amd64 +0 -0
  4. package/dist_rust/rustdns_linux_arm64 +0 -0
  5. package/dist_ts/00_commitinfo_data.js +1 -1
  6. package/dist_ts_client/classes.dnsclient.d.ts +14 -32
  7. package/dist_ts_client/classes.dnsclient.js +63 -55
  8. package/dist_ts_client/classes.rustdnsclientbridge.d.ts +59 -0
  9. package/dist_ts_client/classes.rustdnsclientbridge.js +110 -0
  10. package/dist_ts_client/index.d.ts +1 -0
  11. package/dist_ts_client/index.js +2 -1
  12. package/dist_ts_client/plugins.d.ts +8 -2
  13. package/dist_ts_client/plugins.js +8 -3
  14. package/dist_ts_server/classes.dnsserver.d.ts +38 -57
  15. package/dist_ts_server/classes.dnsserver.js +180 -702
  16. package/dist_ts_server/classes.rustdnsbridge.d.ts +119 -0
  17. package/dist_ts_server/classes.rustdnsbridge.js +136 -0
  18. package/dist_ts_server/index.d.ts +1 -0
  19. package/dist_ts_server/index.js +2 -1
  20. package/dist_ts_server/plugins.d.ts +8 -8
  21. package/dist_ts_server/plugins.js +7 -9
  22. package/npmextra.json +18 -6
  23. package/package.json +19 -19
  24. package/readme.hints.md +30 -4
  25. package/readme.md +345 -645
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/readme.md +47 -0
  28. package/dist_ts_client/dnsly.plugins.d.ts +0 -10
  29. package/dist_ts_client/dnsly.plugins.js +0 -12
  30. package/dist_ts_server/classes.dnssec.d.ts +0 -31
  31. package/dist_ts_server/classes.dnssec.js +0 -155
  32. package/dist_ts_server/classes.dnstools.d.ts +0 -31
  33. package/dist_ts_server/classes.dnstools.js +0 -139
package/readme.md CHANGED
@@ -1,218 +1,253 @@
1
1
  # @push.rocks/smartdns
2
2
 
3
- A robust TypeScript library providing advanced DNS management and resolution capabilities including support for DNSSEC, custom DNS servers, and integration with various DNS providers.
3
+ A TypeScript-first DNS toolkit powered by high-performance Rust binaries covering everything from simple record lookups to running a full authoritative DNS server with DNSSEC, DNS-over-HTTPS, and automatic Let's Encrypt certificates.
4
4
 
5
- ## Install
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
6
8
 
7
- To install `@push.rocks/smartdns`, use the following command with pnpm:
9
+ ## Install
8
10
 
9
11
  ```bash
10
- pnpm install @push.rocks/smartdns --save
12
+ pnpm install @push.rocks/smartdns
11
13
  ```
12
14
 
13
- Or with npm:
15
+ ## Architecture at a Glance 🏗️
14
16
 
15
- ```bash
16
- npm install @push.rocks/smartdns --save
17
- ```
17
+ smartdns ships as **three entry points** that you can import independently:
18
18
 
19
- Make sure you have a TypeScript environment set up to utilize the library effectively.
19
+ | Entry point | What it does |
20
+ |---|---|
21
+ | `@push.rocks/smartdns/client` | DNS resolution & record queries (UDP, DoH, system resolver) |
22
+ | `@push.rocks/smartdns/server` | Full DNS server — UDP, DoH, DNSSEC, ACME |
23
+ | `@push.rocks/smartdns` | Convenience re-export of both modules |
20
24
 
21
- ## Usage
25
+ Both the **client** and the **server** delegate performance-critical work to compiled **Rust binaries** that ship with the package:
22
26
 
23
- `@push.rocks/smartdns` is a comprehensive library that provides both DNS client and server capabilities, leveraging TypeScript for enhanced development experience. The library is organized into three modules:
27
+ - **`rustdns`** The server binary: network I/O, packet parsing, DNSSEC signing
28
+ - **`rustdns-client`** — The client binary: UDP wire-format queries, RFC 8484 DoH resolution
24
29
 
25
- - **Client Module** (`@push.rocks/smartdns/client`): DNS resolution and record queries
26
- - **Server Module** (`@push.rocks/smartdns/server`): Full DNS server implementation with DNSSEC
27
- - **Main Module** (`@push.rocks/smartdns`): Convenience exports for both client and server
30
+ TypeScript retains the public API, handler registration, ACME orchestration, and strategy routing. Communication between TypeScript and Rust happens over stdin/stdout JSON IPC via [`@push.rocks/smartrust`](https://code.foss.global/push.rocks/smartrust).
28
31
 
29
- ### Getting Started
32
+ ```
33
+ ┌─────────────────────────┐
34
+ │ Your Application │
35
+ └────────┬────────────────┘
36
+
37
+ ┌────────────────────┼─────────────────────┐
38
+ ▼ ▼
39
+ ┌──────────────────┐ ┌──────────────────┐
40
+ │ Smartdns Client │ │ DnsServer │
41
+ │ (TypeScript API) │ │ (TypeScript API) │
42
+ └────────┬─────────┘ └────────┬─────────┘
43
+ │ │
44
+ ┌───────┼────────┐ ┌───────────┤
45
+ ▼ ▼ ▼ ▼ ▼
46
+ system Rust Rust Rust binary TS Handlers
47
+ (Node) UDP DoH (rustdns) (minimatch)
48
+ │ │ │
49
+ ▼ ▼ ▼
50
+ rustdns-client UDP / HTTPS listeners
51
+ (IPC binary) DNSSEC signing
52
+ ```
30
53
 
31
- You can import the modules based on your needs:
54
+ ## Usage
55
+
56
+ ### Quick Start
32
57
 
33
58
  ```typescript
34
- // For DNS client operations
59
+ // DNS client — resolve records
35
60
  import { Smartdns } from '@push.rocks/smartdns/client';
36
61
 
37
- // For DNS server operations
38
- import { DnsServer } from '@push.rocks/smartdns/server';
62
+ const dns = new Smartdns({});
63
+ const records = await dns.getRecordsA('example.com');
64
+ console.log(records);
39
65
 
40
- // Or import from the main module (note the different syntax)
41
- import { dnsClientMod, dnsServerMod } from '@push.rocks/smartdns';
42
- const dnsClient = new dnsClientMod.Smartdns({});
43
- const dnsServer = new dnsServerMod.DnsServer({ /* options */ });
44
- ```
66
+ // DNS server serve records
67
+ import { DnsServer } from '@push.rocks/smartdns/server';
45
68
 
46
- ### DNS Client Operations
69
+ const server = new DnsServer({
70
+ udpPort: 5333,
71
+ httpsPort: 8443,
72
+ httpsKey: '...pem...',
73
+ httpsCert: '...pem...',
74
+ dnssecZone: 'example.com',
75
+ });
47
76
 
48
- The DNS client (`Smartdns` class) provides methods to query various DNS record types using DNS-over-HTTPS (DoH) with Cloudflare as the primary provider, with fallback to Node.js DNS resolver.
77
+ server.registerHandler('*.example.com', ['A'], (question) => ({
78
+ name: question.name,
79
+ type: 'A',
80
+ class: 'IN',
81
+ ttl: 300,
82
+ data: '192.168.1.100',
83
+ }));
49
84
 
50
- #### Fetching A Records
85
+ await server.start();
86
+ ```
51
87
 
52
- To fetch "A" records (IPv4 addresses) for a domain:
88
+ Or import from the unified entry point:
53
89
 
54
90
  ```typescript
55
- import { Smartdns } from '@push.rocks/smartdns/client';
91
+ import { dnsClientMod, dnsServerMod } from '@push.rocks/smartdns';
56
92
 
57
- const dnsClient = new Smartdns({});
58
- const aRecords = await dnsClient.getRecordsA('example.com');
59
- console.log(aRecords);
60
- // Output: [{ name: 'example.com', type: 'A', dnsSecEnabled: false, value: '93.184.215.14' }]
93
+ const client = new dnsClientMod.Smartdns({});
94
+ const server = new dnsServerMod.DnsServer({ /* ... */ });
61
95
  ```
62
96
 
63
- #### Fetching AAAA Records
64
-
65
- For resolving a domain to IPv6 addresses:
97
+ ---
66
98
 
67
- ```typescript
68
- const aaaaRecords = await dnsClient.getRecordsAAAA('example.com');
69
- console.log(aaaaRecords);
70
- // Output: [{ name: 'example.com', type: 'AAAA', dnsSecEnabled: false, value: '2606:2800:21f:cb07:6820:80da:af6b:8b2c' }]
71
- ```
99
+ ## 🔍 DNS Client
72
100
 
73
- #### Fetching TXT Records
101
+ The `Smartdns` class resolves DNS records using a configurable strategy that combines the system resolver, raw UDP queries, and DNS-over-HTTPS — all backed by a Rust binary for the wire-format transports.
74
102
 
75
- TXT records store text data, commonly used for domain verification, SPF records, and other metadata:
103
+ ### Constructor Options
76
104
 
77
105
  ```typescript
78
- const txtRecords = await dnsClient.getRecordsTxt('example.com');
79
- console.log(txtRecords);
80
- // Output: [{ name: 'example.com', type: 'TXT', dnsSecEnabled: false, value: 'v=spf1 -all' }]
106
+ interface ISmartDnsConstructorOptions {
107
+ strategy?: 'doh' | 'udp' | 'system' | 'prefer-system' | 'prefer-udp'; // default: 'prefer-system'
108
+ allowDohFallback?: boolean; // fallback to DoH when system fails (default: true)
109
+ timeoutMs?: number; // per-query timeout in milliseconds
110
+ }
81
111
  ```
82
112
 
83
- #### Other Record Types
113
+ ### Resolution Strategies
114
+
115
+ | Strategy | Behavior |
116
+ |---|---|
117
+ | `prefer-system` | 🏠 Try the OS resolver first, fall back to Rust DoH. Honors `/etc/hosts`. |
118
+ | `system` | 🏠 Use only the Node.js system resolver. No Rust binary needed. |
119
+ | `doh` | 🌐 Use only DNS-over-HTTPS (RFC 8484 wire format via Cloudflare). Rust-powered. |
120
+ | `udp` | ⚡ Use only raw UDP queries to upstream resolver (Cloudflare 1.1.1.1). Rust-powered. |
121
+ | `prefer-udp` | ⚡ Try Rust UDP first, fall back to Rust DoH if UDP fails. |
84
122
 
85
- The client supports various other DNS record types:
123
+ The Rust binary (`rustdns-client`) is spawned **lazily** only on the first query that needs it. This means `system`-only usage incurs zero Rust overhead.
124
+
125
+ ### Querying Records
86
126
 
87
127
  ```typescript
88
- // MX records for mail servers
89
- const mxRecords = await dnsClient.getRecords('example.com', 'MX');
128
+ const dns = new Smartdns({ strategy: 'prefer-udp' });
129
+
130
+ // Type-specific helpers
131
+ const aRecords = await dns.getRecordsA('example.com');
132
+ const aaaaRecords = await dns.getRecordsAAAA('example.com');
133
+ const txtRecords = await dns.getRecordsTxt('example.com');
90
134
 
91
- // NS records for nameservers
92
- const nsRecords = await dnsClient.getNameServers('example.com');
135
+ // Generic query — supports A, AAAA, CNAME, MX, TXT, NS, SOA, PTR, SRV
136
+ const mxRecords = await dns.getRecords('example.com', 'MX');
93
137
 
94
- // Generic query method with retry support
95
- const records = await dnsClient.getRecords('example.com', 'CNAME', { retryCount: 3 });
138
+ // Nameserver lookup
139
+ const nameservers = await dns.getNameServers('example.com');
96
140
  ```
97
141
 
98
- ### Advanced DNS Features
142
+ Every query returns an array of `IDnsRecord`:
99
143
 
100
- #### Checking DNS Propagation
144
+ ```typescript
145
+ interface IDnsRecord {
146
+ name: string;
147
+ type: string; // 'A', 'AAAA', 'TXT', 'MX', etc.
148
+ dnsSecEnabled: boolean; // true if upstream AD flag was set
149
+ value: string;
150
+ }
151
+ ```
101
152
 
102
- The client provides a powerful method to verify DNS propagation globally, essential when making DNS changes:
153
+ ### DNSSEC Detection 🔐
154
+
155
+ When using `doh`, `udp`, or `prefer-udp` strategies, the Rust binary sends queries with the EDNS0 DO (DNSSEC OK) bit set and reports the AD (Authenticated Data) flag from the upstream response:
103
156
 
104
157
  ```typescript
105
- // Check if a specific DNS record has propagated
106
- const recordType = 'TXT';
107
- const expectedValue = 'verification=abc123';
158
+ const dns = new Smartdns({ strategy: 'udp' });
159
+ const records = await dns.getRecordsA('cloudflare.com');
160
+ console.log(records[0].dnsSecEnabled); // true — upstream validated DNSSEC
161
+ ```
108
162
 
109
- const isAvailable = await dnsClient.checkUntilAvailable(
163
+ ### Checking DNS Propagation
164
+
165
+ Wait for a specific record to appear — essential after making DNS changes:
166
+
167
+ ```typescript
168
+ const propagated = await dns.checkUntilAvailable(
110
169
  'example.com',
111
- recordType,
112
- expectedValue,
113
- 50, // Number of check cycles (default: 50)
114
- 500 // Interval between checks in ms (default: 500)
170
+ 'TXT',
171
+ 'verification=abc123',
172
+ 50, // max check cycles (default: 50)
173
+ 500 // interval in ms (default: 500)
115
174
  );
116
175
 
117
- if (isAvailable) {
118
- console.log('DNS record has propagated successfully!');
119
- } else {
120
- console.log('DNS propagation timeout - record not found.');
176
+ if (propagated) {
177
+ console.log('Record is live!');
121
178
  }
122
179
  ```
123
180
 
124
- #### Configuring System DNS Provider
181
+ The method alternates between system resolver and the configured strategy on each cycle for maximum coverage.
182
+
183
+ ### Configuring the System DNS Provider
125
184
 
126
- You can configure Node.js to use a specific DNS provider for all DNS queries:
185
+ Override the global Node.js DNS resolver for all subsequent lookups:
127
186
 
128
187
  ```typescript
129
- // Import the standalone function
130
188
  import { makeNodeProcessUseDnsProvider } from '@push.rocks/smartdns/client';
131
189
 
132
- // Use Cloudflare DNS for all Node.js DNS operations
133
- makeNodeProcessUseDnsProvider('cloudflare');
134
-
135
- // Or use Google DNS
136
- makeNodeProcessUseDnsProvider('google');
190
+ makeNodeProcessUseDnsProvider('cloudflare'); // 1.1.1.1 / 1.0.0.1
191
+ makeNodeProcessUseDnsProvider('google'); // 8.8.8.8 / 8.8.4.4
137
192
  ```
138
193
 
139
- ### Real-World Use Cases
140
-
141
- #### DNS-Based Feature Flagging
194
+ ### Cleanup
142
195
 
143
- Use TXT records for dynamic feature toggles without redeployment:
196
+ When you're done with a `Smartdns` instance (especially one using Rust strategies), call `destroy()` to kill the Rust child process:
144
197
 
145
198
  ```typescript
146
- const txtRecords = await dnsClient.getRecordsTxt('features.example.com');
147
- const featureFlags = {};
199
+ const dns = new Smartdns({ strategy: 'udp' });
200
+ // ... do queries ...
201
+ dns.destroy(); // kills rustdns-client process
202
+ ```
148
203
 
149
- txtRecords.forEach(record => {
150
- // Parse TXT records like "feature-dark-mode=true"
151
- const [feature, enabled] = record.value.split('=');
152
- featureFlags[feature] = enabled === 'true';
153
- });
204
+ ---
154
205
 
155
- if (featureFlags['feature-dark-mode']) {
156
- console.log('Dark mode is enabled!');
157
- }
158
- ```
206
+ ## 🖥️ DNS Server
159
207
 
160
- #### Service Discovery
208
+ The `DnsServer` class runs a production-capable authoritative DNS server backed by a Rust binary. It supports standard UDP DNS (port 53), DNS-over-HTTPS, DNSSEC signing, and automated Let's Encrypt certificates.
161
209
 
162
- Use DNS for service endpoint discovery:
210
+ ### Server Options
163
211
 
164
212
  ```typescript
165
- // Discover API endpoints via TXT records
166
- const serviceRecords = await dnsClient.getRecordsTxt('_services.example.com');
167
-
168
- // Discover mail servers
169
- const mxRecords = await dnsClient.getRecords('example.com', 'MX');
170
- const primaryMailServer = mxRecords
171
- .sort((a, b) => a.priority - b.priority)[0]?.exchange;
213
+ interface IDnsServerOptions {
214
+ udpPort: number; // Port for UDP DNS queries
215
+ httpsPort: number; // Port for DNS-over-HTTPS
216
+ httpsKey: string; // PEM private key (path or content)
217
+ httpsCert: string; // PEM certificate (path or content)
218
+ dnssecZone: string; // Zone for DNSSEC signing
219
+ primaryNameserver?: string; // SOA mname field (default: 'ns1.{dnssecZone}')
220
+ udpBindInterface?: string; // IP to bind UDP (default: '0.0.0.0')
221
+ httpsBindInterface?: string; // IP to bind HTTPS (default: '0.0.0.0')
222
+ manualUdpMode?: boolean; // Don't auto-bind UDP socket
223
+ manualHttpsMode?: boolean; // Don't auto-bind HTTPS server
224
+ enableLocalhostHandling?: boolean; // RFC 6761 localhost (default: true)
225
+ }
172
226
  ```
173
227
 
174
- ### DNS Server Implementation
175
-
176
- The `DnsServer` class provides a full-featured DNS server with support for UDP, DNS-over-HTTPS (DoH), DNSSEC, and automatic SSL certificate management via Let's Encrypt.
177
-
178
- #### Basic DNS Server Setup
179
-
180
- Create a simple DNS server that responds to queries:
228
+ ### Basic Server
181
229
 
182
230
  ```typescript
183
231
  import { DnsServer } from '@push.rocks/smartdns/server';
184
232
 
185
- const dnsServer = new DnsServer({
186
- udpPort: 5333, // UDP port for DNS queries
187
- httpsPort: 8443, // HTTPS port for DNS-over-HTTPS
188
- httpsKey: 'path/to/key.pem', // Required for HTTPS
189
- httpsCert: 'path/to/cert.pem', // Required for HTTPS
190
- dnssecZone: 'example.com' // Optional: enable DNSSEC for this zone
191
- });
192
-
193
- // For enhanced security, bind to specific interfaces
194
- const secureServer = new DnsServer({
195
- udpPort: 53,
196
- httpsPort: 443,
197
- httpsKey: 'path/to/key.pem',
198
- httpsCert: 'path/to/cert.pem',
233
+ const server = new DnsServer({
234
+ udpPort: 5333,
235
+ httpsPort: 8443,
236
+ httpsKey: '...pem...',
237
+ httpsCert: '...pem...',
199
238
  dnssecZone: 'example.com',
200
- udpBindInterface: '127.0.0.1', // Bind UDP to localhost only
201
- httpsBindInterface: '127.0.0.1', // Bind HTTPS to localhost only
202
- primaryNameserver: 'ns1.example.com' // Optional: primary nameserver for SOA records (defaults to ns1.{dnssecZone})
203
239
  });
204
240
 
205
- // Register a handler for all subdomains of example.com
206
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
241
+ // Register handlers
242
+ server.registerHandler('example.com', ['A'], (question) => ({
207
243
  name: question.name,
208
244
  type: 'A',
209
245
  class: 'IN',
210
246
  ttl: 300,
211
- data: '192.168.1.100',
247
+ data: '93.184.215.14',
212
248
  }));
213
249
 
214
- // Register a handler for TXT records
215
- dnsServer.registerHandler('example.com', ['TXT'], (question) => ({
250
+ server.registerHandler('example.com', ['TXT'], (question) => ({
216
251
  name: question.name,
217
252
  type: 'TXT',
218
253
  class: 'IN',
@@ -220,643 +255,308 @@ dnsServer.registerHandler('example.com', ['TXT'], (question) => ({
220
255
  data: 'v=spf1 include:_spf.example.com ~all',
221
256
  }));
222
257
 
223
- // Start the server
224
- await dnsServer.start();
225
- console.log('DNS Server started!');
258
+ await server.start();
259
+ // DNS Server started (UDP: 0.0.0.0:5333, HTTPS: 0.0.0.0:8443)
226
260
  ```
227
261
 
228
- ### SOA Records and Primary Nameserver
262
+ ### Handler System 🎯
229
263
 
230
- The DNS server automatically generates SOA (Start of Authority) records for zones when no specific handler matches a query. The SOA record contains important zone metadata including the primary nameserver.
264
+ Handlers use **glob patterns** (via `minimatch`) to match incoming query names. Multiple handlers can contribute records to the same response.
231
265
 
232
266
  ```typescript
233
- const dnsServer = new DnsServer({
234
- udpPort: 53,
235
- httpsPort: 443,
236
- httpsKey: 'path/to/key.pem',
237
- httpsCert: 'path/to/cert.pem',
238
- dnssecZone: 'example.com',
239
- primaryNameserver: 'ns1.example.com' // Specify your actual primary nameserver
240
- });
241
-
242
- // Without primaryNameserver, the SOA mname defaults to 'ns1.{dnssecZone}'
243
- // In this case, it would be 'ns1.example.com'
244
-
245
- // The automatic SOA record includes:
246
- // - mname: Primary nameserver (from primaryNameserver option)
247
- // - rname: Responsible person email (hostmaster.{dnssecZone})
248
- // - serial: Unix timestamp
249
- // - refresh: 3600 (1 hour)
250
- // - retry: 600 (10 minutes)
251
- // - expire: 604800 (7 days)
252
- // - minimum: 86400 (1 day)
253
- ```
267
+ // Exact domain
268
+ server.registerHandler('example.com', ['A'], handler);
254
269
 
255
- **Important**: Even if you have multiple nameservers (NS records), only one is designated as the primary in the SOA record. All authoritative nameservers should return the same SOA record.
270
+ // All subdomains
271
+ server.registerHandler('*.example.com', ['A'], handler);
256
272
 
257
- ### DNSSEC Support
258
-
259
- The DNS server includes comprehensive DNSSEC support with automatic key generation and record signing:
260
-
261
- ```typescript
262
- import { DnsServer } from '@push.rocks/smartdns/server';
263
-
264
- const dnsServer = new DnsServer({
265
- udpPort: 53,
266
- httpsPort: 443,
267
- dnssecZone: 'secure.example.com', // Enable DNSSEC for this zone
273
+ // Specific pattern
274
+ server.registerHandler('db-*.internal.example.com', ['A'], (question) => {
275
+ const id = question.name.match(/db-(\d+)/)?.[1];
276
+ return {
277
+ name: question.name,
278
+ type: 'A',
279
+ class: 'IN',
280
+ ttl: 60,
281
+ data: `10.0.1.${id}`,
282
+ };
268
283
  });
269
284
 
270
- // The server automatically:
271
- // 1. Generates DNSKEY records with ECDSA (algorithm 13)
272
- // 2. Creates DS records for parent zone delegation
273
- // 3. Signs all responses with RRSIG records
274
- // 4. Provides NSEC records for authenticated denial of existence
275
-
276
- // Register your handlers as normal - DNSSEC signing is automatic
277
- dnsServer.registerHandler('secure.example.com', ['A'], (question) => ({
285
+ // Catch-all
286
+ server.registerHandler('*', ['A'], (question) => ({
278
287
  name: question.name,
279
288
  type: 'A',
280
289
  class: 'IN',
281
290
  ttl: 300,
282
- data: '192.168.1.1',
291
+ data: '127.0.0.1',
283
292
  }));
284
293
 
285
- await dnsServer.start();
294
+ // Multiple record types
295
+ server.registerHandler('example.com', ['MX'], (question) => ({
296
+ name: question.name,
297
+ type: 'MX',
298
+ class: 'IN',
299
+ ttl: 300,
300
+ data: { preference: 10, exchange: 'mail.example.com' },
301
+ }));
286
302
 
287
- // Query for DNSSEC records
288
- import { Smartdns } from '@push.rocks/smartdns/client';
289
- const client = new Smartdns({});
290
- const dnskeyRecords = await client.getRecords('secure.example.com', 'DNSKEY');
291
- const dsRecords = await client.getRecords('secure.example.com', 'DS');
303
+ // Unregister a handler
304
+ server.unregisterHandler('example.com', ['A']);
292
305
  ```
293
306
 
294
- #### Supported DNSSEC Algorithms
307
+ When no handler matches, the server automatically returns an **SOA record** for the zone.
295
308
 
296
- The server supports multiple DNSSEC algorithms:
297
- - **ECDSAP256SHA256** (Algorithm 13) - Default, using P-256 curve
298
- - **ED25519** (Algorithm 15) - Modern elliptic curve algorithm
299
- - **RSASHA256** (Algorithm 8) - RSA-based signatures
309
+ ### DNSSEC
300
310
 
301
- ### Let's Encrypt Integration
311
+ DNSSEC is enabled automatically when you set the `dnssecZone` option. The Rust backend handles:
302
312
 
303
- The DNS server includes built-in Let's Encrypt support for automatic SSL certificate management:
313
+ - **Key generation** ECDSA P-256 (algorithm 13) by default
314
+ - **DNSKEY / DS record** generation
315
+ - **RRSIG signing** for all responses
316
+ - **NSEC records** for authenticated denial of existence
304
317
 
305
318
  ```typescript
306
- import { DnsServer } from '@push.rocks/smartdns/server';
307
-
308
- const dnsServer = new DnsServer({
319
+ const server = new DnsServer({
309
320
  udpPort: 53,
310
321
  httpsPort: 443,
311
- httpsKey: '/path/to/letsencrypt/key.pem', // Will be auto-generated
312
- httpsCert: '/path/to/letsencrypt/cert.pem', // Will be auto-generated
322
+ httpsKey: '...',
323
+ httpsCert: '...',
324
+ dnssecZone: 'secure.example.com',
313
325
  });
314
326
 
315
- // Retrieve Let's Encrypt certificate for your domain
316
- const result = await dnsServer.retrieveSslCertificate(
317
- ['secure.example.com', 'www.secure.example.com'],
318
- {
319
- email: 'admin@example.com',
320
- staging: false, // Use production Let's Encrypt
321
- certDir: './certs'
322
- }
323
- );
324
-
325
- if (result.success) {
326
- console.log('Certificate retrieved successfully!');
327
- }
328
-
329
- // The server automatically:
330
- // 1. Handles ACME DNS-01 challenges
331
- // 2. Creates temporary TXT records for domain validation
332
- // 3. Retrieves and installs the certificate
333
- // 4. Restarts the HTTPS server with the new certificate
327
+ // Just register handlers as usual signing is automatic
328
+ server.registerHandler('secure.example.com', ['A'], (q) => ({
329
+ name: q.name,
330
+ type: 'A',
331
+ class: 'IN',
332
+ ttl: 300,
333
+ data: '10.0.0.1',
334
+ }));
334
335
 
335
- await dnsServer.start();
336
- console.log('DNS Server with Let\'s Encrypt SSL started!');
336
+ await server.start();
337
337
  ```
338
338
 
339
- ### Manual Socket Handling
339
+ Supported algorithms: **ECDSAP256SHA256** (13), **ED25519** (15), **RSASHA256** (8).
340
340
 
341
- The DNS server supports manual socket handling for advanced use cases like clustering, load balancing, and custom transport implementations. You can control UDP and HTTPS socket handling independently.
342
-
343
- #### Configuration Options
344
-
345
- ```typescript
346
- export interface IDnsServerOptions {
347
- httpsKey: string; // Path or content of HTTPS private key
348
- httpsCert: string; // Path or content of HTTPS certificate
349
- httpsPort: number; // Port for DNS-over-HTTPS
350
- udpPort: number; // Port for standard UDP DNS
351
- dnssecZone: string; // Zone name for DNSSEC signing
352
- udpBindInterface?: string; // IP address to bind UDP socket (default: '0.0.0.0')
353
- httpsBindInterface?: string; // IP address to bind HTTPS server (default: '0.0.0.0')
354
- manualUdpMode?: boolean; // Handle UDP sockets manually
355
- manualHttpsMode?: boolean; // Handle HTTPS sockets manually
356
- primaryNameserver?: string; // Primary nameserver for SOA records (default: 'ns1.{dnssecZone}')
357
- }
358
- ```
341
+ ### SOA Records
359
342
 
360
- #### Basic Manual Socket Usage
343
+ The server auto-generates SOA records for zones when no specific handler matches. Customize the primary nameserver:
361
344
 
362
345
  ```typescript
363
- import { DnsServer } from '@push.rocks/smartdns/server';
364
- import * as dgram from 'dgram';
365
- import * as net from 'net';
366
-
367
- // Create server with manual UDP mode
368
- const dnsServer = new DnsServer({
369
- httpsKey: '...',
370
- httpsCert: '...',
371
- httpsPort: 853,
372
- udpPort: 53,
346
+ const server = new DnsServer({
347
+ // ...
373
348
  dnssecZone: 'example.com',
374
- manualUdpMode: true // UDP manual, HTTPS automatic
349
+ primaryNameserver: 'ns1.example.com', // defaults to 'ns1.{dnssecZone}'
375
350
  });
376
351
 
377
- await dnsServer.start(); // HTTPS binds, UDP doesn't
378
-
379
- // Create your own UDP socket
380
- const udpSocket = dgram.createSocket('udp4');
381
-
382
- // Handle incoming UDP messages
383
- udpSocket.on('message', (msg, rinfo) => {
384
- dnsServer.handleUdpMessage(msg, rinfo, (response, responseRinfo) => {
385
- // Send response using your socket
386
- udpSocket.send(response, responseRinfo.port, responseRinfo.address);
387
- });
388
- });
389
-
390
- // Bind to custom port or multiple interfaces
391
- udpSocket.bind(5353, '0.0.0.0');
352
+ // Generated SOA includes:
353
+ // mname: ns1.example.com
354
+ // rname: hostmaster.example.com
355
+ // serial: Unix timestamp
356
+ // refresh: 3600, retry: 600, expire: 604800, minimum: 86400
392
357
  ```
393
358
 
394
- #### Manual HTTPS Socket Handling
359
+ ### Let's Encrypt Integration 🔒
360
+
361
+ Built-in ACME DNS-01 challenge support for automatic SSL certificates:
395
362
 
396
363
  ```typescript
397
- // Create server with manual HTTPS mode
398
- const dnsServer = new DnsServer({
399
- httpsKey: '...',
400
- httpsCert: '...',
401
- httpsPort: 853,
364
+ const server = new DnsServer({
402
365
  udpPort: 53,
366
+ httpsPort: 443,
367
+ httpsKey: '/path/to/key.pem',
368
+ httpsCert: '/path/to/cert.pem',
403
369
  dnssecZone: 'example.com',
404
- manualHttpsMode: true // HTTPS manual, UDP automatic
405
370
  });
406
371
 
407
- await dnsServer.start(); // UDP binds, HTTPS doesn't
372
+ await server.start();
408
373
 
409
- // Create your own TCP server
410
- const tcpServer = net.createServer((socket) => {
411
- // Pass TCP sockets to DNS server
412
- dnsServer.handleHttpsSocket(socket);
413
- });
374
+ const result = await server.retrieveSslCertificate(
375
+ ['example.com', 'www.example.com'],
376
+ {
377
+ email: 'admin@example.com',
378
+ staging: false,
379
+ certDir: './certs',
380
+ }
381
+ );
414
382
 
415
- tcpServer.listen(8853, '0.0.0.0');
383
+ if (result.success) {
384
+ console.log('Certificate installed!');
385
+ // The server automatically:
386
+ // 1. Registers temporary _acme-challenge TXT handlers
387
+ // 2. Completes DNS-01 validation
388
+ // 3. Updates the HTTPS server with the new cert
389
+ // 4. Cleans up challenge handlers
390
+ }
416
391
  ```
417
392
 
418
- #### Full Manual Mode
393
+ ### Interface Binding
419
394
 
420
- Control both protocols manually for complete flexibility:
395
+ Restrict the server to specific network interfaces:
421
396
 
422
397
  ```typescript
423
- const dnsServer = new DnsServer({
424
- httpsKey: '...',
425
- httpsCert: '...',
426
- httpsPort: 853,
427
- udpPort: 53,
428
- dnssecZone: 'example.com',
429
- manualUdpMode: true,
430
- manualHttpsMode: true
398
+ // Localhost only great for development
399
+ const server = new DnsServer({
400
+ // ...
401
+ udpBindInterface: '127.0.0.1',
402
+ httpsBindInterface: '127.0.0.1',
431
403
  });
432
404
 
433
- await dnsServer.start(); // Neither protocol binds
434
-
435
- // Set up your own socket handling for both protocols
436
- // Perfect for custom routing, load balancing, or clustering
405
+ // Different interfaces per protocol
406
+ const server = new DnsServer({
407
+ // ...
408
+ udpBindInterface: '192.168.1.100',
409
+ httpsBindInterface: '10.0.0.50',
410
+ });
437
411
  ```
438
412
 
439
- #### Advanced Use Cases
413
+ ### Manual Socket Handling 🔧
440
414
 
441
- ##### Load Balancing Across Multiple UDP Sockets
415
+ For clustering, load balancing, or custom transports, take control of socket management:
442
416
 
443
417
  ```typescript
444
- // Create multiple UDP sockets for different CPU cores
445
- const sockets = [];
446
- const numCPUs = require('os').cpus().length;
447
-
448
- for (let i = 0; i < numCPUs; i++) {
449
- const socket = dgram.createSocket({
450
- type: 'udp4',
451
- reuseAddr: true // Allow multiple sockets on same port
452
- });
453
-
454
- socket.on('message', (msg, rinfo) => {
455
- dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
456
- socket.send(response, rinfo.port, rinfo.address);
457
- });
458
- });
459
-
460
- socket.bind(53);
461
- sockets.push(socket);
462
- }
463
- ```
464
-
465
- ##### Clustering with Worker Processes
466
-
467
- ```typescript
468
- import cluster from 'cluster';
469
418
  import { DnsServer } from '@push.rocks/smartdns/server';
419
+ import * as dgram from 'dgram';
470
420
 
471
- if (cluster.isPrimary) {
472
- // Master process accepts connections
473
- const server = net.createServer({ pauseOnConnect: true });
474
-
475
- // Distribute connections to workers
476
- server.on('connection', (socket) => {
477
- const worker = getNextWorker(); // Round-robin or custom logic
478
- worker.send('socket', socket);
479
- });
480
-
481
- server.listen(853);
482
- } else {
483
- // Worker process handles DNS
484
- const dnsServer = new DnsServer({
485
- httpsKey: '...',
486
- httpsCert: '...',
487
- httpsPort: 853,
488
- udpPort: 53,
489
- dnssecZone: 'example.com',
490
- manualHttpsMode: true
491
- });
492
-
493
- process.on('message', (msg, socket) => {
494
- if (msg === 'socket') {
495
- dnsServer.handleHttpsSocket(socket);
496
- }
497
- });
498
-
499
- await dnsServer.start();
500
- }
501
- ```
502
-
503
- ##### Custom Transport Protocol
504
-
505
- ```typescript
506
- // Use DNS server with custom transport (e.g., WebSocket)
507
- import WebSocket from 'ws';
508
-
509
- const wss = new WebSocket.Server({ port: 8080 });
510
- const dnsServer = new DnsServer({
511
- httpsKey: '...',
512
- httpsCert: '...',
513
- httpsPort: 853,
514
- udpPort: 53,
515
- dnssecZone: 'example.com',
421
+ // Manual UDP mode — you control the socket
422
+ const server = new DnsServer({
423
+ // ...
516
424
  manualUdpMode: true,
517
- manualHttpsMode: true
518
425
  });
519
426
 
520
- await dnsServer.start();
427
+ await server.start(); // HTTPS auto-binds, UDP does not
521
428
 
522
- wss.on('connection', (ws) => {
523
- ws.on('message', (data) => {
524
- // Process DNS query from WebSocket
525
- const response = dnsServer.processRawDnsPacket(Buffer.from(data));
526
- ws.send(response);
429
+ const socket = dgram.createSocket('udp4');
430
+ socket.on('message', (msg, rinfo) => {
431
+ server.handleUdpMessage(msg, rinfo, (response, responseRinfo) => {
432
+ socket.send(response, responseRinfo.port, responseRinfo.address);
527
433
  });
528
434
  });
435
+ socket.bind(5353);
529
436
  ```
530
437
 
531
- ##### Multi-Interface Binding
438
+ Full manual mode (both protocols):
532
439
 
533
440
  ```typescript
534
- // Bind to multiple network interfaces manually
535
- const interfaces = [
536
- { address: '192.168.1.100', type: 'udp4' },
537
- { address: '10.0.0.50', type: 'udp4' },
538
- { address: '::1', type: 'udp6' }
539
- ];
540
-
541
- interfaces.forEach(({ address, type }) => {
542
- const socket = dgram.createSocket(type);
543
-
544
- socket.on('message', (msg, rinfo) => {
545
- console.log(`Query received on ${address}`);
546
- dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
547
- socket.send(response, rinfo.port, rinfo.address);
548
- });
549
- });
550
-
551
- socket.bind(53, address);
441
+ const server = new DnsServer({
442
+ // ...
443
+ manualUdpMode: true,
444
+ manualHttpsMode: true,
552
445
  });
553
- ```
554
-
555
- ### Handling Different Protocols
556
446
 
557
- #### UDP DNS Server
447
+ await server.start(); // Neither protocol binds automatically
448
+ ```
558
449
 
559
- Traditional DNS queries over UDP (port 53):
450
+ Process individual DNS packets directly:
560
451
 
561
452
  ```typescript
562
- import { DnsServer } from '@push.rocks/smartdns/server';
563
- import * as plugins from '@push.rocks/smartdns/server/plugins';
564
-
565
- const dnsServer = new DnsServer({
566
- udpPort: 5353, // Using alternate port for testing
567
- httpsPort: 8443,
568
- httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
569
- httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
570
- dnssecZone: 'test.local' // Optional
571
- });
453
+ // Synchronous (TypeScript fallback)
454
+ const response = server.processRawDnsPacket(packetBuffer);
572
455
 
573
- // The UDP server automatically handles DNS packet parsing and encoding
574
- dnsServer.registerHandler('test.local', ['A'], (question) => ({
575
- name: question.name,
576
- type: 'A',
577
- class: 'IN',
578
- ttl: 60,
579
- data: '127.0.0.1',
580
- }));
581
-
582
- await dnsServer.start();
583
-
584
- // Test with dig or nslookup:
585
- // dig @localhost -p 5353 test.local
456
+ // Asynchronous (via Rust bridge includes DNSSEC signing)
457
+ const response = await server.processRawDnsPacketAsync(packetBuffer);
586
458
  ```
587
459
 
588
- #### DNS-over-HTTPS (DoH) Server
589
-
590
- Provide encrypted DNS queries over HTTPS:
460
+ #### Load Balancing Example
591
461
 
592
462
  ```typescript
593
- import { DnsServer } from '@push.rocks/smartdns/server';
594
- import * as fs from 'fs';
595
-
596
- const dnsServer = new DnsServer({
597
- httpsPort: 8443,
598
- httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
599
- httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
600
- });
463
+ import * as dgram from 'dgram';
464
+ import * as os from 'os';
601
465
 
602
- // The HTTPS server automatically handles:
603
- // - DNS wire format in POST body
604
- // - Proper Content-Type headers (application/dns-message)
605
- // - Base64url encoding for GET requests
466
+ const numCPUs = os.cpus().length;
606
467
 
607
- dnsServer.registerHandler('secure.local', ['A'], (question) => ({
608
- name: question.name,
609
- type: 'A',
610
- class: 'IN',
611
- ttl: 300,
612
- data: '10.0.0.1',
613
- }));
468
+ for (let i = 0; i < numCPUs; i++) {
469
+ const socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
614
470
 
615
- await dnsServer.start();
471
+ socket.on('message', (msg, rinfo) => {
472
+ server.handleUdpMessage(msg, rinfo, (response, rinfo) => {
473
+ socket.send(response, rinfo.port, rinfo.address);
474
+ });
475
+ });
616
476
 
617
- // Test with curl:
618
- // curl -H "Content-Type: application/dns-message" \
619
- // --data-binary @query.bin \
620
- // https://localhost:8443/dns-query
477
+ socket.bind(53);
478
+ }
621
479
  ```
622
480
 
623
- ### Interface Binding
624
-
625
- For enhanced security and network isolation, you can bind the DNS server to specific network interfaces instead of all available interfaces.
626
-
627
- #### Localhost-Only Binding
628
-
629
- Bind to localhost for development or local-only DNS services:
481
+ ### Stopping the Server
630
482
 
631
483
  ```typescript
632
- const localServer = new DnsServer({
633
- udpPort: 5353,
634
- httpsPort: 8443,
635
- httpsKey: cert.key,
636
- httpsCert: cert.cert,
637
- dnssecZone: 'local.test',
638
- udpBindInterface: '127.0.0.1', // IPv4 localhost
639
- httpsBindInterface: '127.0.0.1'
640
- });
641
-
642
- // Or use IPv6 localhost
643
- const ipv6LocalServer = new DnsServer({
644
- // ... other options
645
- udpBindInterface: '::1', // IPv6 localhost
646
- httpsBindInterface: '::1'
647
- });
484
+ await server.stop();
648
485
  ```
649
486
 
650
- #### Specific Interface Binding
487
+ This gracefully shuts down the Rust process and releases all bound sockets.
651
488
 
652
- Bind to a specific network interface in multi-homed servers:
489
+ ---
653
490
 
654
- ```typescript
655
- const interfaceServer = new DnsServer({
656
- udpPort: 53,
657
- httpsPort: 443,
658
- httpsKey: cert.key,
659
- httpsCert: cert.cert,
660
- dnssecZone: 'example.com',
661
- udpBindInterface: '192.168.1.100', // Specific internal interface
662
- httpsBindInterface: '10.0.0.50' // Different interface for HTTPS
663
- });
664
- ```
491
+ ## 🦀 Rust Crate Structure
665
492
 
666
- #### Security Considerations
493
+ The Rust workspace (`rust/crates/`) contains five crates:
667
494
 
668
- - **Default Behavior**: If not specified, servers bind to all interfaces (`0.0.0.0`)
669
- - **Localhost Binding**: Use `127.0.0.1` or `::1` for development and testing
670
- - **Production**: Consider binding to specific internal interfaces for security
671
- - **Validation**: Invalid IP addresses will throw an error during server startup
495
+ | Crate | Purpose |
496
+ |---|---|
497
+ | `rustdns` | Server binary IPC management loop, handler callback routing |
498
+ | `rustdns-client` | Client binary stateless UDP/DoH query proxy |
499
+ | `rustdns-protocol` | DNS wire format parsing, encoding, and RDATA decode/encode |
500
+ | `rustdns-server` | Async UDP + HTTPS servers (tokio, hyper, rustls) |
501
+ | `rustdns-dnssec` | ECDSA/ED25519 key generation and RRset signing |
672
502
 
673
- ### Advanced Handler Patterns
503
+ Pre-compiled binaries for `linux_amd64` and `linux_arm64` are included in `dist_rust/`. Cross-compilation is handled by [`@git.zone/tsrust`](https://code.foss.global/git.zone/tsrust).
674
504
 
675
- #### Pattern-Based Routing
505
+ ---
676
506
 
677
- Use glob patterns for flexible domain matching:
678
-
679
- ```typescript
680
- // Match all subdomains
681
- dnsServer.registerHandler('*.example.com', ['A'], (question) => {
682
- // Extract subdomain
683
- const subdomain = question.name.replace('.example.com', '');
684
-
685
- // Dynamic response based on subdomain
686
- return {
687
- name: question.name,
688
- type: 'A',
689
- class: 'IN',
690
- ttl: 300,
691
- data: subdomain === 'api' ? '10.0.0.10' : '10.0.0.1',
692
- };
693
- });
507
+ ## 🧪 Testing
694
508
 
695
- // Match specific patterns
696
- dnsServer.registerHandler('db-*.service.local', ['A'], (question) => {
697
- const instanceId = question.name.match(/db-(\d+)/)?.[1];
698
- return {
699
- name: question.name,
700
- type: 'A',
701
- class: 'IN',
702
- ttl: 60,
703
- data: `10.0.1.${instanceId}`,
704
- };
705
- });
509
+ ```bash
510
+ # Run all tests
511
+ pnpm test
706
512
 
707
- // Catch-all handler
708
- dnsServer.registerHandler('*', ['A'], (question) => ({
709
- name: question.name,
710
- type: 'A',
711
- class: 'IN',
712
- ttl: 300,
713
- data: '127.0.0.1',
714
- }));
513
+ # Run specific test file
514
+ tstest test/test.client.ts --verbose
515
+ tstest test/test.server.ts --verbose
715
516
  ```
716
517
 
717
- ### Testing
718
-
719
- The library uses `@git.zone/tstest` for testing. Here's an example of comprehensive tests:
518
+ Example test:
720
519
 
721
520
  ```typescript
722
521
  import { expect, tap } from '@git.zone/tstest/tapbundle';
723
522
  import { Smartdns } from '@push.rocks/smartdns/client';
724
- import { DnsServer } from '@push.rocks/smartdns/server';
725
-
726
- // Test DNS Client
727
- tap.test('DNS Client - Query Records', async () => {
728
- const dnsClient = new Smartdns({});
729
-
730
- // Test A record query
731
- const aRecords = await dnsClient.getRecordsA('google.com');
732
- expect(aRecords).toBeArray();
733
- expect(aRecords[0]).toHaveProperty('type', 'A');
734
- expect(aRecords[0].data).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
735
-
736
- // Test TXT record query
737
- const txtRecords = await dnsClient.getRecordsTxt('google.com');
738
- expect(txtRecords).toBeArray();
739
- expect(txtRecords[0]).toHaveProperty('type', 'TXT');
740
- });
741
-
742
- // Test DNS Server
743
- let dnsServer: DnsServer;
744
-
745
- tap.test('DNS Server - Setup and Start', async () => {
746
- dnsServer = new DnsServer({
747
- udpPort: 5353,
748
- httpsPort: 8443,
749
- httpsKey: 'test-key', // Use test certificates
750
- httpsCert: 'test-cert',
751
- dnssecZone: 'test.local'
752
- });
753
-
754
- expect(dnsServer).toBeInstanceOf(DnsServer);
755
- await dnsServer.start();
756
- });
757
-
758
- tap.test('DNS Server - Register Handlers', async () => {
759
- // Register multiple handlers
760
- dnsServer.registerHandler('test.local', ['A'], () => ({
761
- name: 'test.local',
762
- type: 'A',
763
- class: 'IN',
764
- ttl: 300,
765
- data: '127.0.0.1',
766
- }));
767
-
768
- dnsServer.registerHandler('*.test.local', ['A'], (question) => ({
769
- name: question.name,
770
- type: 'A',
771
- class: 'IN',
772
- ttl: 60,
773
- data: '127.0.0.2',
774
- }));
775
- });
776
523
 
777
- tap.test('DNS Server - Query via UDP', async (tools) => {
778
- const dnsPacket = (await import('dns-packet')).default;
779
- const dgram = await import('dgram');
780
-
781
- const query = dnsPacket.encode({
782
- type: 'query',
783
- id: 1234,
784
- questions: [{
785
- type: 'A',
786
- class: 'IN',
787
- name: 'test.local',
788
- }],
789
- });
790
-
791
- const client = dgram.createSocket('udp4');
792
- const done = tools.defer();
793
-
794
- client.on('message', (msg) => {
795
- const response = dnsPacket.decode(msg);
796
- expect(response.answers[0].data).toEqual('127.0.0.1');
797
- client.close();
798
- done.resolve();
799
- });
800
-
801
- client.send(query, 5353, 'localhost'); // Use the port specified during server creation
802
- await done.promise;
524
+ tap.test('resolve A records via UDP', async () => {
525
+ const dns = new Smartdns({ strategy: 'udp' });
526
+ const records = await dns.getRecordsA('google.com');
527
+ expect(records).toBeArray();
528
+ expect(records[0]).toHaveProperty('type', 'A');
529
+ expect(records[0]).toHaveProperty('value');
530
+ dns.destroy();
803
531
  });
804
532
 
805
- tap.test('DNS Server - Cleanup', async () => {
806
- await dnsServer.stop();
533
+ tap.test('detect DNSSEC via DoH', async () => {
534
+ const dns = new Smartdns({ strategy: 'doh' });
535
+ const records = await dns.getRecordsA('cloudflare.com');
536
+ expect(records[0].dnsSecEnabled).toBeTrue();
537
+ dns.destroy();
807
538
  });
808
539
 
809
- // Run tests
810
- await tap.start();
540
+ export default tap.start();
811
541
  ```
812
542
 
813
- ### Best Practices
814
-
815
- 1. **Port Selection**: Use non-privileged ports (>1024) during development
816
- 2. **Handler Organization**: Group related handlers together
817
- 3. **Error Handling**: Always handle DNS query errors gracefully
818
- 4. **DNSSEC**: Enable DNSSEC for production deployments
819
- 5. **Monitoring**: Log DNS queries for debugging and analytics
820
- 6. **Rate Limiting**: Implement rate limiting for public DNS servers
821
- 7. **Caching**: Respect TTL values and implement proper caching
822
- 8. **Manual Sockets**: Use manual socket handling for clustering and load balancing
823
-
824
- ### Performance Considerations
825
-
826
- - The DNS client uses HTTP keep-alive for connection reuse
827
- - The DNS server handles concurrent UDP and HTTPS requests efficiently
828
- - DNSSEC signatures are generated on-demand to reduce memory usage
829
- - Pattern matching uses caching for improved performance
830
- - Manual socket handling enables horizontal scaling across CPU cores
831
-
832
- ### Security Considerations
833
-
834
- - Always use DNSSEC for authenticated responses
835
- - Enable DoH for encrypted DNS queries
836
- - Validate and sanitize all DNS inputs
837
- - Implement access controls for DNS server handlers
838
- - Use Let's Encrypt for automatic SSL certificate management
839
- - Never expose internal network information through DNS
840
- - Bind to specific interfaces in production environments
841
- - Use manual socket handling for custom security layers
842
-
843
- This comprehensive library provides everything needed for both DNS client operations and running production-grade DNS servers with modern security features in TypeScript.
844
-
845
543
  ## License and Legal Information
846
544
 
847
- This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license.md) file within this repository.
545
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
848
546
 
849
547
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
850
548
 
851
549
  ### Trademarks
852
550
 
853
- This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
551
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
552
+
553
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
854
554
 
855
555
  ### Company Information
856
556
 
857
- Task Venture Capital GmbH
858
- Registered at District court Bremen HRB 35230 HB, Germany
557
+ Task Venture Capital GmbH
558
+ Registered at District Court Bremen HRB 35230 HB, Germany
859
559
 
860
- For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
560
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
861
561
 
862
- By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
562
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.