@push.rocks/smartdns 7.0.2 → 7.4.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/readme.md CHANGED
@@ -1,392 +1,813 @@
1
1
  # @push.rocks/smartdns
2
2
 
3
- A TypeScript library for smart DNS methods, supporting various DNS records and providers.
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.
4
4
 
5
5
  ## Install
6
6
 
7
- To install `@push.rocks/smartdns`, use the following command with npm:
7
+ To install `@push.rocks/smartdns`, use the following command with pnpm:
8
8
 
9
9
  ```bash
10
- npm install @push.rocks/smartdns --save
10
+ pnpm install @push.rocks/smartdns --save
11
11
  ```
12
12
 
13
- Or with `yarn`:
13
+ Or with npm:
14
14
 
15
15
  ```bash
16
- yarn add @push.rocks/smartdns
16
+ npm install @push.rocks/smartdns --save
17
17
  ```
18
18
 
19
19
  Make sure you have a TypeScript environment set up to utilize the library effectively.
20
20
 
21
21
  ## Usage
22
22
 
23
- `@push.rocks/smartdns` is a comprehensive library aimed at facilitating smart DNS operations, leveraging TypeScript for enhanced development experience. This section aims to cover several real-world scenarios demonstrating the library's capabilities, from basic DNS lookups to more advanced DNS management tasks.
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:
24
+
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
24
28
 
25
29
  ### Getting Started
26
30
 
27
- First, ensure you import the module into your TypeScript project:
31
+ You can import the modules based on your needs:
28
32
 
29
33
  ```typescript
30
- import { Smartdns } from '@push.rocks/smartdns';
34
+ // For DNS client operations
35
+ import { Smartdns } from '@push.rocks/smartdns/client';
36
+
37
+ // For DNS server operations
38
+ import { DnsServer } from '@push.rocks/smartdns/server';
39
+
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 */ });
31
44
  ```
32
45
 
33
- ### Basic DNS Record Lookup
46
+ ### DNS Client Operations
34
47
 
35
- Often, the need arises to fetch various DNS records for a domain. `@push.rocks/smartdns` simplifies this by providing intuitive methods. A DNS record is essentially a map from a domain name to information about that domain, such as its IP address, mail exchange server, etc.
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.
36
49
 
37
50
  #### Fetching A Records
38
51
 
39
- To fetch an "A" record for a domain, which resolves the domain to an IPv4 address, use the following approach:
52
+ To fetch "A" records (IPv4 addresses) for a domain:
40
53
 
41
54
  ```typescript
42
- import { Smartdns } from '@push.rocks/smartdns';
55
+ import { Smartdns } from '@push.rocks/smartdns/client';
43
56
 
44
- const dnsManager = new Smartdns({});
45
- const aRecords = await dnsManager.getRecordsA('example.com');
57
+ const dnsClient = new Smartdns({});
58
+ const aRecords = await dnsClient.getRecordsA('example.com');
46
59
  console.log(aRecords);
60
+ // Output: [{ name: 'example.com', type: 'A', dnsSecEnabled: false, value: '93.184.215.14' }]
47
61
  ```
48
62
 
49
- This will return an array of records that include the IPv4 addresses mapped to the domain `example.com`.
50
-
51
63
  #### Fetching AAAA Records
52
64
 
53
- For resolving a domain to an IPv6 address, you can fetch "AAAA" records in a similar manner:
65
+ For resolving a domain to IPv6 addresses:
54
66
 
55
67
  ```typescript
56
- const aaaaRecords = await dnsManager.getRecordsAAAA('example.com');
68
+ const aaaaRecords = await dnsClient.getRecordsAAAA('example.com');
57
69
  console.log(aaaaRecords);
70
+ // Output: [{ name: 'example.com', type: 'AAAA', dnsSecEnabled: false, value: '2606:2800:21f:cb07:6820:80da:af6b:8b2c' }]
58
71
  ```
59
72
 
60
- These queries are quite common where IPv6 addresses have been prevalent due to the scarcity of IPv4 addresses.
61
-
62
73
  #### Fetching TXT Records
63
74
 
64
- TXT records store arbitrary text data associated with a domain. They are often used to hold information such as SPF records for email validation or Google site verification token.
75
+ TXT records store text data, commonly used for domain verification, SPF records, and other metadata:
65
76
 
66
77
  ```typescript
67
- const txtRecords = await dnsManager.getRecordsTxt('example.com');
78
+ const txtRecords = await dnsClient.getRecordsTxt('example.com');
68
79
  console.log(txtRecords);
80
+ // Output: [{ name: 'example.com', type: 'TXT', dnsSecEnabled: false, value: 'v=spf1 -all' }]
69
81
  ```
70
82
 
71
- TXT records have increasingly become significant with the growth of security features and integrations that require domain verification.
83
+ #### Other Record Types
72
84
 
73
- ### Advanced DNS Management
85
+ The client supports various other DNS record types:
74
86
 
75
- The `@push.rocks/smartdns` doesn't just stop at querying— it offers more advanced DNS management utilities, which are crucial for real-world applications involving DNS operations.
87
+ ```typescript
88
+ // MX records for mail servers
89
+ const mxRecords = await dnsClient.getRecords('example.com', 'MX');
90
+
91
+ // NS records for nameservers
92
+ const nsRecords = await dnsClient.getNameServers('example.com');
93
+
94
+ // Generic query method with retry support
95
+ const records = await dnsClient.getRecords('example.com', 'CNAME', { retryCount: 3 });
96
+ ```
97
+
98
+ ### Advanced DNS Features
76
99
 
77
100
  #### Checking DNS Propagation
78
101
 
79
- When changing DNS records, ensuring that the new records have propagated fully is crucial. `@push.rocks/smartdns` facilitates this with a method to check if a DNS record is available globally. This can be critical for ensuring that users worldwide are able to access your updated records in a consistent manner.
102
+ The client provides a powerful method to verify DNS propagation globally, essential when making DNS changes:
80
103
 
81
104
  ```typescript
82
- const recordType = 'TXT'; // Record type: A, AAAA, CNAME, TXT etc.
83
- const expectedValue = 'your_expected_value';
84
- const isAvailable = await dnsManager.checkUntilAvailable('example.com', recordType, expectedValue);
105
+ // Check if a specific DNS record has propagated
106
+ const recordType = 'TXT';
107
+ const expectedValue = 'verification=abc123';
108
+
109
+ const isAvailable = await dnsClient.checkUntilAvailable(
110
+ 'example.com',
111
+ recordType,
112
+ expectedValue,
113
+ 50, // Number of check cycles (default: 50)
114
+ 500 // Interval between checks in ms (default: 500)
115
+ );
85
116
 
86
117
  if (isAvailable) {
87
- console.log('Record propagated successfully.');
118
+ console.log('DNS record has propagated successfully!');
88
119
  } else {
89
- console.log('Record propagation failed or timed out.');
120
+ console.log('DNS propagation timeout - record not found.');
90
121
  }
91
122
  ```
92
123
 
93
- This method repeatedly queries DNS servers until the expected DNS record appears, making sure that the changes made are visible globally.
124
+ #### Configuring System DNS Provider
94
125
 
95
- ### Leveraging DNS for Application Logic
126
+ You can configure Node.js to use a specific DNS provider for all DNS queries:
96
127
 
97
- DNS records can function beyond their typical usage of domain-to-IP resolution. They can be extremely useful in application logic such as feature flagging or environment-specific configurations.
128
+ ```typescript
129
+ // Import the standalone function
130
+ import { makeNodeProcessUseDnsProvider } from '@push.rocks/smartdns/client';
131
+
132
+ // Use Cloudflare DNS for all Node.js DNS operations
133
+ makeNodeProcessUseDnsProvider('cloudflare');
98
134
 
99
- #### Example: Feature Flagging via TXT Records
135
+ // Or use Google DNS
136
+ makeNodeProcessUseDnsProvider('google');
137
+ ```
100
138
 
101
- One such advanced use case is using TXT records for enabling or disabling features dynamically without needing to redeploy or change the actual application code:
139
+ ### Real-World Use Cases
140
+
141
+ #### DNS-Based Feature Flagging
142
+
143
+ Use TXT records for dynamic feature toggles without redeployment:
102
144
 
103
145
  ```typescript
104
- const txtRecords = await dnsManager.getRecordsTxt('features.example.com');
105
- const featureFlags = txtRecords.reduce((acc, record) => {
106
- const [flag, isEnabled] = record.value.split('=');
107
- acc[flag] = isEnabled === 'true';
108
- return acc;
109
- }, {});
110
-
111
- if (featureFlags['NewFeature']) {
112
- // Logic to enable the new feature
113
- console.log('New Feature enabled');
146
+ const txtRecords = await dnsClient.getRecordsTxt('features.example.com');
147
+ const featureFlags = {};
148
+
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
+ });
154
+
155
+ if (featureFlags['feature-dark-mode']) {
156
+ console.log('Dark mode is enabled!');
114
157
  }
115
158
  ```
116
159
 
117
- This approach enables applications to be more flexible and responsive to business needs as feature toggles can be managed through DNS.
160
+ #### Service Discovery
161
+
162
+ Use DNS for service endpoint discovery:
163
+
164
+ ```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;
172
+ ```
118
173
 
119
174
  ### DNS Server Implementation
120
175
 
121
- `@push.rocks/smartdns` includes powerful features that allow you to implement your very own DNS server, complete with UDP and HTTPS protocols support and DNSSEC compliance.
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.
122
177
 
123
- #### Basic DNS Server Example
178
+ #### Basic DNS Server Setup
124
179
 
125
- Implementing a DNS server involves setting it up to respond to various DNS queries. Here's how you can set up a basic DNS server using UDP and HTTPS protocols:
180
+ Create a simple DNS server that responds to queries:
126
181
 
127
182
  ```typescript
128
- import { DnsServer } from '@push.rocks/smartdns';
183
+ import { DnsServer } from '@push.rocks/smartdns/server';
129
184
 
130
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,
131
197
  httpsKey: 'path/to/key.pem',
132
198
  httpsCert: 'path/to/cert.pem',
133
- httpsPort: 443,
134
- udpPort: 53,
135
199
  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
136
202
  });
137
203
 
204
+ // Register a handler for all subdomains of example.com
138
205
  dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
139
206
  name: question.name,
140
207
  type: 'A',
141
208
  class: 'IN',
142
209
  ttl: 300,
143
- data: '127.0.0.1',
210
+ data: '192.168.1.100',
144
211
  }));
145
212
 
146
- dnsServer.start().then(() => console.log('DNS Server started'));
147
- ```
213
+ // Register a handler for TXT records
214
+ dnsServer.registerHandler('example.com', ['TXT'], (question) => ({
215
+ name: question.name,
216
+ type: 'TXT',
217
+ class: 'IN',
218
+ ttl: 300,
219
+ data: 'v=spf1 include:_spf.example.com ~all',
220
+ }));
148
221
 
149
- This sets up a basic DNS server responding to A records for the domain `example.com` and mirrors the common structure used in production applications.
222
+ // Start the server
223
+ await dnsServer.start();
224
+ console.log('DNS Server started!');
225
+ ```
150
226
 
151
227
  ### DNSSEC Support
152
228
 
153
- DNS Security Extensions (DNSSEC) adds an additional layer of security to DNS, protecting against various types of attacks. With `@push.rocks/smartdns`, setting up a DNS server with DNSSEC is straightforward.
154
-
155
- #### DNSSEC Configuration
156
-
157
- To configure DNSSEC for your DNS server, you’ll need to establish DNSSEC parameters including zone signatures and enabling key management. This setup ensures that DNS records are signed and can be verified for authenticity.
229
+ The DNS server includes comprehensive DNSSEC support with automatic key generation and record signing:
158
230
 
159
231
  ```typescript
160
- import { DnsServer } from '@push.rocks/smartdns';
232
+ import { DnsServer } from '@push.rocks/smartdns/server';
161
233
 
162
234
  const dnsServer = new DnsServer({
163
- httpsKey: 'path/to/key.pem',
164
- httpsCert: 'path/to/cert.pem',
165
- httpsPort: 443,
166
235
  udpPort: 53,
167
- dnssecZone: 'example.com',
236
+ httpsPort: 443,
237
+ dnssecZone: 'secure.example.com', // Enable DNSSEC for this zone
168
238
  });
169
239
 
170
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
240
+ // The server automatically:
241
+ // 1. Generates DNSKEY records with ECDSA (algorithm 13)
242
+ // 2. Creates DS records for parent zone delegation
243
+ // 3. Signs all responses with RRSIG records
244
+ // 4. Provides NSEC records for authenticated denial of existence
245
+
246
+ // Register your handlers as normal - DNSSEC signing is automatic
247
+ dnsServer.registerHandler('secure.example.com', ['A'], (question) => ({
171
248
  name: question.name,
172
249
  type: 'A',
173
250
  class: 'IN',
174
251
  ttl: 300,
175
- data: '127.0.0.1',
252
+ data: '192.168.1.1',
176
253
  }));
177
254
 
178
- dnsServer.start().then(() => console.log('DNS Server with DNSSEC started'));
255
+ await dnsServer.start();
256
+
257
+ // Query for DNSSEC records
258
+ import { Smartdns } from '@push.rocks/smartdns/client';
259
+ const client = new Smartdns({});
260
+ const dnskeyRecords = await client.getRecords('secure.example.com', 'DNSKEY');
261
+ const dsRecords = await client.getRecords('secure.example.com', 'DS');
179
262
  ```
180
263
 
181
- ### Handling DNS Queries Over Different Protocols
264
+ #### Supported DNSSEC Algorithms
182
265
 
183
- The library supports handling DNS queries over UDP and HTTPS. This functionality allows for the flexible use and management of DNS inquiries and resolves, accommodating various protocol needs.
266
+ The server supports multiple DNSSEC algorithms:
267
+ - **ECDSAP256SHA256** (Algorithm 13) - Default, using P-256 curve
268
+ - **ED25519** (Algorithm 15) - Modern elliptic curve algorithm
269
+ - **RSASHA256** (Algorithm 8) - RSA-based signatures
184
270
 
185
- #### Handling UDP Queries
271
+ ### Let's Encrypt Integration
186
272
 
187
- UDP is the traditional DNS protocol used for quick, non-persistent queries. Here’s how you can set up a DNS server to respond to UDP queries:
273
+ The DNS server includes built-in Let's Encrypt support for automatic SSL certificate management:
188
274
 
189
275
  ```typescript
190
- import { DnsServer } from '@push.rocks/smartdns';
191
- import dgram from 'dgram';
276
+ import { DnsServer } from '@push.rocks/smartdns/server';
192
277
 
193
278
  const dnsServer = new DnsServer({
194
279
  udpPort: 53,
195
280
  httpsPort: 443,
281
+ httpsKey: '/path/to/letsencrypt/key.pem', // Will be auto-generated
282
+ httpsCert: '/path/to/letsencrypt/cert.pem', // Will be auto-generated
196
283
  });
197
284
 
198
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
199
- name: question.name,
200
- type: 'A',
201
- class: 'IN',
202
- ttl: 300,
203
- data: '127.0.0.1',
204
- }));
285
+ // Retrieve Let's Encrypt certificate for your domain
286
+ const result = await dnsServer.retrieveSslCertificate(
287
+ ['secure.example.com', 'www.secure.example.com'],
288
+ {
289
+ email: 'admin@example.com',
290
+ staging: false, // Use production Let's Encrypt
291
+ certDir: './certs'
292
+ }
293
+ );
205
294
 
206
- dnsServer.start().then(() => {
207
- console.log('UDP DNS Server started on port', dnsServer.getOptions().udpPort);
295
+ if (result.success) {
296
+ console.log('Certificate retrieved successfully!');
297
+ }
298
+
299
+ // The server automatically:
300
+ // 1. Handles ACME DNS-01 challenges
301
+ // 2. Creates temporary TXT records for domain validation
302
+ // 3. Retrieves and installs the certificate
303
+ // 4. Restarts the HTTPS server with the new certificate
304
+
305
+ await dnsServer.start();
306
+ console.log('DNS Server with Let\'s Encrypt SSL started!');
307
+ ```
308
+
309
+ ### Manual Socket Handling
310
+
311
+ 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.
312
+
313
+ #### Configuration Options
314
+
315
+ ```typescript
316
+ export interface IDnsServerOptions {
317
+ // ... standard options ...
318
+ manualUdpMode?: boolean; // Handle UDP sockets manually
319
+ manualHttpsMode?: boolean; // Handle HTTPS sockets manually
320
+ }
321
+ ```
322
+
323
+ #### Basic Manual Socket Usage
324
+
325
+ ```typescript
326
+ import { DnsServer } from '@push.rocks/smartdns/server';
327
+ import * as dgram from 'dgram';
328
+ import * as net from 'net';
329
+
330
+ // Create server with manual UDP mode
331
+ const dnsServer = new DnsServer({
332
+ httpsKey: '...',
333
+ httpsCert: '...',
334
+ httpsPort: 853,
335
+ udpPort: 53,
336
+ dnssecZone: 'example.com',
337
+ manualUdpMode: true // UDP manual, HTTPS automatic
338
+ });
339
+
340
+ await dnsServer.start(); // HTTPS binds, UDP doesn't
341
+
342
+ // Create your own UDP socket
343
+ const udpSocket = dgram.createSocket('udp4');
344
+
345
+ // Handle incoming UDP messages
346
+ udpSocket.on('message', (msg, rinfo) => {
347
+ dnsServer.handleUdpMessage(msg, rinfo, (response, responseRinfo) => {
348
+ // Send response using your socket
349
+ udpSocket.send(response, responseRinfo.port, responseRinfo.address);
350
+ });
351
+ });
352
+
353
+ // Bind to custom port or multiple interfaces
354
+ udpSocket.bind(5353, '0.0.0.0');
355
+ ```
356
+
357
+ #### Manual HTTPS Socket Handling
358
+
359
+ ```typescript
360
+ // Create server with manual HTTPS mode
361
+ const dnsServer = new DnsServer({
362
+ httpsKey: '...',
363
+ httpsCert: '...',
364
+ httpsPort: 853,
365
+ udpPort: 53,
366
+ dnssecZone: 'example.com',
367
+ manualHttpsMode: true // HTTPS manual, UDP automatic
368
+ });
369
+
370
+ await dnsServer.start(); // UDP binds, HTTPS doesn't
371
+
372
+ // Create your own TCP server
373
+ const tcpServer = net.createServer((socket) => {
374
+ // Pass TCP sockets to DNS server
375
+ dnsServer.handleHttpsSocket(socket);
208
376
  });
209
377
 
210
- const client = dgram.createSocket('udp4');
378
+ tcpServer.listen(8853, '0.0.0.0');
379
+ ```
380
+
381
+ #### Full Manual Mode
211
382
 
212
- client.on('message', (msg, rinfo) => {
213
- console.log(`Received ${msg} from ${rinfo.address}:${rinfo.port}`);
383
+ Control both protocols manually for complete flexibility:
384
+
385
+ ```typescript
386
+ const dnsServer = new DnsServer({
387
+ httpsKey: '...',
388
+ httpsCert: '...',
389
+ httpsPort: 853,
390
+ udpPort: 53,
391
+ dnssecZone: 'example.com',
392
+ manualUdpMode: true,
393
+ manualHttpsMode: true
214
394
  });
215
395
 
216
- client.send(Buffer.from('example DNS query'), dnsServer.getOptions().udpPort, 'localhost');
396
+ await dnsServer.start(); // Neither protocol binds
397
+
398
+ // Set up your own socket handling for both protocols
399
+ // Perfect for custom routing, load balancing, or clustering
400
+ ```
401
+
402
+ #### Advanced Use Cases
403
+
404
+ ##### Load Balancing Across Multiple UDP Sockets
405
+
406
+ ```typescript
407
+ // Create multiple UDP sockets for different CPU cores
408
+ const sockets = [];
409
+ const numCPUs = require('os').cpus().length;
410
+
411
+ for (let i = 0; i < numCPUs; i++) {
412
+ const socket = dgram.createSocket({
413
+ type: 'udp4',
414
+ reuseAddr: true // Allow multiple sockets on same port
415
+ });
416
+
417
+ socket.on('message', (msg, rinfo) => {
418
+ dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
419
+ socket.send(response, rinfo.port, rinfo.address);
420
+ });
421
+ });
422
+
423
+ socket.bind(53);
424
+ sockets.push(socket);
425
+ }
217
426
  ```
218
427
 
219
- This segment of code creates a UDP server that listens for incoming DNS requests and responds accordingly.
428
+ ##### Clustering with Worker Processes
220
429
 
221
- #### Handling HTTPS Queries
430
+ ```typescript
431
+ import cluster from 'cluster';
432
+ import { DnsServer } from '@push.rocks/smartdns/server';
433
+
434
+ if (cluster.isPrimary) {
435
+ // Master process accepts connections
436
+ const server = net.createServer({ pauseOnConnect: true });
437
+
438
+ // Distribute connections to workers
439
+ server.on('connection', (socket) => {
440
+ const worker = getNextWorker(); // Round-robin or custom logic
441
+ worker.send('socket', socket);
442
+ });
443
+
444
+ server.listen(853);
445
+ } else {
446
+ // Worker process handles DNS
447
+ const dnsServer = new DnsServer({
448
+ httpsKey: '...',
449
+ httpsCert: '...',
450
+ httpsPort: 853,
451
+ udpPort: 53,
452
+ dnssecZone: 'example.com',
453
+ manualHttpsMode: true
454
+ });
455
+
456
+ process.on('message', (msg, socket) => {
457
+ if (msg === 'socket') {
458
+ dnsServer.handleHttpsSocket(socket);
459
+ }
460
+ });
461
+
462
+ await dnsServer.start();
463
+ }
464
+ ```
222
465
 
223
- DNS over HTTPS (DoH) offers a heightened level of privacy and security, where DNS queries are transmitted over HTTPS to prevent eavesdropping and man-in-the-middle attacks.
466
+ ##### Custom Transport Protocol
224
467
 
225
468
  ```typescript
226
- import { DnsServer } from '@push.rocks/smartdns';
227
- import https from 'https';
228
- import fs from 'fs';
469
+ // Use DNS server with custom transport (e.g., WebSocket)
470
+ import WebSocket from 'ws';
229
471
 
472
+ const wss = new WebSocket.Server({ port: 8080 });
230
473
  const dnsServer = new DnsServer({
231
- httpsKey: fs.readFileSync('path/to/key.pem'),
232
- httpsCert: fs.readFileSync('path/to/cert.pem'),
233
- httpsPort: 443,
474
+ httpsKey: '...',
475
+ httpsCert: '...',
476
+ httpsPort: 853,
234
477
  udpPort: 53,
235
478
  dnssecZone: 'example.com',
479
+ manualUdpMode: true,
480
+ manualHttpsMode: true
236
481
  });
237
482
 
238
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
483
+ await dnsServer.start();
484
+
485
+ wss.on('connection', (ws) => {
486
+ ws.on('message', (data) => {
487
+ // Process DNS query from WebSocket
488
+ const response = dnsServer.processRawDnsPacket(Buffer.from(data));
489
+ ws.send(response);
490
+ });
491
+ });
492
+ ```
493
+
494
+ ##### Multi-Interface Binding
495
+
496
+ ```typescript
497
+ // Bind to multiple network interfaces manually
498
+ const interfaces = [
499
+ { address: '192.168.1.100', type: 'udp4' },
500
+ { address: '10.0.0.50', type: 'udp4' },
501
+ { address: '::1', type: 'udp6' }
502
+ ];
503
+
504
+ interfaces.forEach(({ address, type }) => {
505
+ const socket = dgram.createSocket(type);
506
+
507
+ socket.on('message', (msg, rinfo) => {
508
+ console.log(`Query received on ${address}`);
509
+ dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
510
+ socket.send(response, rinfo.port, rinfo.address);
511
+ });
512
+ });
513
+
514
+ socket.bind(53, address);
515
+ });
516
+ ```
517
+
518
+ ### Handling Different Protocols
519
+
520
+ #### UDP DNS Server
521
+
522
+ Traditional DNS queries over UDP (port 53):
523
+
524
+ ```typescript
525
+ import { DnsServer } from '@push.rocks/smartdns/server';
526
+ import * as plugins from '@push.rocks/smartdns/server/plugins';
527
+
528
+ const dnsServer = new DnsServer({
529
+ udpPort: 5353, // Using alternate port for testing
530
+ httpsPort: 8443,
531
+ httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
532
+ httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
533
+ dnssecZone: 'test.local' // Optional
534
+ });
535
+
536
+ // The UDP server automatically handles DNS packet parsing and encoding
537
+ dnsServer.registerHandler('test.local', ['A'], (question) => ({
239
538
  name: question.name,
240
- type: 'A',
539
+ type: 'A',
241
540
  class: 'IN',
242
- ttl: 300,
541
+ ttl: 60,
243
542
  data: '127.0.0.1',
244
543
  }));
245
544
 
246
- dnsServer.start().then(() => console.log('HTTPS DNS Server started'));
545
+ await dnsServer.start();
247
546
 
248
- const client = https.request({
249
- hostname: 'localhost',
250
- port: 443,
251
- path: '/dns-query',
252
- method: 'POST',
253
- headers: {
254
- 'Content-Type': 'application/dns-message'
255
- }
256
- }, (res) => {
257
- res.on('data', (d) => {
258
- process.stdout.write(d);
259
- });
260
- });
547
+ // Test with dig or nslookup:
548
+ // dig @localhost -p 5353 test.local
549
+ ```
550
+
551
+ #### DNS-over-HTTPS (DoH) Server
552
+
553
+ Provide encrypted DNS queries over HTTPS:
554
+
555
+ ```typescript
556
+ import { DnsServer } from '@push.rocks/smartdns/server';
557
+ import * as fs from 'fs';
261
558
 
262
- client.on('error', (e) => {
263
- console.error(e);
559
+ const dnsServer = new DnsServer({
560
+ httpsPort: 8443,
561
+ httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
562
+ httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
264
563
  });
265
564
 
266
- client.write(Buffer.from('example DNS query'));
267
- client.end();
268
- ```
565
+ // The HTTPS server automatically handles:
566
+ // - DNS wire format in POST body
567
+ // - Proper Content-Type headers (application/dns-message)
568
+ // - Base64url encoding for GET requests
569
+
570
+ dnsServer.registerHandler('secure.local', ['A'], (question) => ({
571
+ name: question.name,
572
+ type: 'A',
573
+ class: 'IN',
574
+ ttl: 300,
575
+ data: '10.0.0.1',
576
+ }));
269
577
 
270
- This ensures that DNS requests can be securely transmitted over the web, maintaining privacy for the clients querying the DNS server.
578
+ await dnsServer.start();
271
579
 
272
- ### Testing
580
+ // Test with curl:
581
+ // curl -H "Content-Type: application/dns-message" \
582
+ // --data-binary @query.bin \
583
+ // https://localhost:8443/dns-query
584
+ ```
273
585
 
274
- Like any crucial application component, DNS servers require thorough testing to ensure reliability and correctness in different scenarios. Here we use TAP (Test Anything Protocol) to test various functionalities.
586
+ ### Interface Binding
275
587
 
276
- #### DNS Server Tests
588
+ For enhanced security and network isolation, you can bind the DNS server to specific network interfaces instead of all available interfaces.
277
589
 
278
- `@push.rocks/smartdns` integrates seamlessly with TAP, allowing for comprehensive testing of server functionalities.
590
+ #### Localhost-Only Binding
279
591
 
280
- Here's an example of how you might set up tests for your DNS server:
592
+ Bind to localhost for development or local-only DNS services:
281
593
 
282
594
  ```typescript
283
- import { expect, tap } from '@push.rocks/tapbundle';
595
+ const localServer = new DnsServer({
596
+ udpPort: 5353,
597
+ httpsPort: 8443,
598
+ httpsKey: cert.key,
599
+ httpsCert: cert.cert,
600
+ dnssecZone: 'local.test',
601
+ udpBindInterface: '127.0.0.1', // IPv4 localhost
602
+ httpsBindInterface: '127.0.0.1'
603
+ });
284
604
 
285
- import { DnsServer } from '@push.rocks/smartdns';
605
+ // Or use IPv6 localhost
606
+ const ipv6LocalServer = new DnsServer({
607
+ // ... other options
608
+ udpBindInterface: '::1', // IPv6 localhost
609
+ httpsBindInterface: '::1'
610
+ });
611
+ ```
286
612
 
287
- let dnsServer: DnsServer;
613
+ #### Specific Interface Binding
288
614
 
289
- tap.test('should create an instance of DnsServer', async () => {
290
- dnsServer = new DnsServer({
291
- httpsKey: 'path/to/key.pem',
292
- httpsCert: 'path/to/cert.pem',
293
- httpsPort: 443,
294
- udpPort: 53,
295
- dnssecZone: 'example.com',
296
- });
297
- expect(dnsServer).toBeInstanceOf(DnsServer);
298
- });
615
+ Bind to a specific network interface in multi-homed servers:
299
616
 
300
- tap.test('should start the server', async () => {
301
- await dnsServer.start();
302
- expect(dnsServer.isRunning()).toBeTrue();
617
+ ```typescript
618
+ const interfaceServer = new DnsServer({
619
+ udpPort: 53,
620
+ httpsPort: 443,
621
+ httpsKey: cert.key,
622
+ httpsCert: cert.cert,
623
+ dnssecZone: 'example.com',
624
+ udpBindInterface: '192.168.1.100', // Specific internal interface
625
+ httpsBindInterface: '10.0.0.50' // Different interface for HTTPS
303
626
  });
627
+ ```
628
+
629
+ #### Security Considerations
630
+
631
+ - **Default Behavior**: If not specified, servers bind to all interfaces (`0.0.0.0`)
632
+ - **Localhost Binding**: Use `127.0.0.1` or `::1` for development and testing
633
+ - **Production**: Consider binding to specific internal interfaces for security
634
+ - **Validation**: Invalid IP addresses will throw an error during server startup
304
635
 
305
- tap.test('should add a DNS handler', async () => {
306
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
636
+ ### Advanced Handler Patterns
637
+
638
+ #### Pattern-Based Routing
639
+
640
+ Use glob patterns for flexible domain matching:
641
+
642
+ ```typescript
643
+ // Match all subdomains
644
+ dnsServer.registerHandler('*.example.com', ['A'], (question) => {
645
+ // Extract subdomain
646
+ const subdomain = question.name.replace('.example.com', '');
647
+
648
+ // Dynamic response based on subdomain
649
+ return {
307
650
  name: question.name,
308
651
  type: 'A',
309
652
  class: 'IN',
310
653
  ttl: 300,
311
- data: '127.0.0.1',
312
- }));
313
-
314
- const response = dnsServer.processDnsRequest({
315
- type: 'query',
316
- id: 1,
317
- flags: 0,
318
- questions: [
319
- {
320
- name: 'test.example.com',
321
- type: 'A',
322
- class: 'IN',
323
- },
324
- ],
325
- answers: [],
326
- });
654
+ data: subdomain === 'api' ? '10.0.0.10' : '10.0.0.1',
655
+ };
656
+ });
327
657
 
328
- expect(response.answers[0]).toEqual({
329
- name: 'test.example.com',
658
+ // Match specific patterns
659
+ dnsServer.registerHandler('db-*.service.local', ['A'], (question) => {
660
+ const instanceId = question.name.match(/db-(\d+)/)?.[1];
661
+ return {
662
+ name: question.name,
330
663
  type: 'A',
331
664
  class: 'IN',
332
- ttl: 300,
333
- data: '127.0.0.1',
334
- });
665
+ ttl: 60,
666
+ data: `10.0.1.${instanceId}`,
667
+ };
335
668
  });
336
669
 
337
- tap.test('should query the server over HTTP', async () => {
338
- const query = dnsPacket.encode({
339
- type: 'query',
340
- id: 2,
341
- flags: dnsPacket.RECURSION_DESIRED,
342
- questions: [
343
- {
344
- name: 'test.example.com',
345
- type: 'A',
346
- class: 'IN',
347
- },
348
- ],
349
- });
670
+ // Catch-all handler
671
+ dnsServer.registerHandler('*', ['A'], (question) => ({
672
+ name: question.name,
673
+ type: 'A',
674
+ class: 'IN',
675
+ ttl: 300,
676
+ data: '127.0.0.1',
677
+ }));
678
+ ```
350
679
 
351
- const response = await fetch('https://localhost:443/dns-query', {
352
- method: 'POST',
353
- body: query,
354
- headers: {
355
- 'Content-Type': 'application/dns-message',
356
- }
357
- });
680
+ ### Testing
681
+
682
+ The library uses `@git.zone/tstest` for testing. Here's an example of comprehensive tests:
683
+
684
+ ```typescript
685
+ import { expect, tap } from '@git.zone/tstest/tapbundle';
686
+ import { Smartdns } from '@push.rocks/smartdns/client';
687
+ import { DnsServer } from '@push.rocks/smartdns/server';
688
+
689
+ // Test DNS Client
690
+ tap.test('DNS Client - Query Records', async () => {
691
+ const dnsClient = new Smartdns({});
692
+
693
+ // Test A record query
694
+ const aRecords = await dnsClient.getRecordsA('google.com');
695
+ expect(aRecords).toBeArray();
696
+ expect(aRecords[0]).toHaveProperty('type', 'A');
697
+ expect(aRecords[0].data).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
698
+
699
+ // Test TXT record query
700
+ const txtRecords = await dnsClient.getRecordsTxt('google.com');
701
+ expect(txtRecords).toBeArray();
702
+ expect(txtRecords[0]).toHaveProperty('type', 'TXT');
703
+ });
358
704
 
359
- expect(response.status).toEqual(200);
705
+ // Test DNS Server
706
+ let dnsServer: DnsServer;
360
707
 
361
- const responseData = await response.arrayBuffer();
362
- const dnsResponse = dnsPacket.decode(Buffer.from(responseData));
708
+ tap.test('DNS Server - Setup and Start', async () => {
709
+ dnsServer = new DnsServer({
710
+ udpPort: 5353,
711
+ httpsPort: 8443,
712
+ httpsKey: 'test-key', // Use test certificates
713
+ httpsCert: 'test-cert',
714
+ dnssecZone: 'test.local'
715
+ });
716
+
717
+ expect(dnsServer).toBeInstanceOf(DnsServer);
718
+ await dnsServer.start();
719
+ });
363
720
 
364
- expect(dnsResponse.answers[0]).toEqual({
365
- name: 'test.example.com',
721
+ tap.test('DNS Server - Register Handlers', async () => {
722
+ // Register multiple handlers
723
+ dnsServer.registerHandler('test.local', ['A'], () => ({
724
+ name: 'test.local',
366
725
  type: 'A',
367
726
  class: 'IN',
368
727
  ttl: 300,
369
728
  data: '127.0.0.1',
729
+ }));
730
+
731
+ dnsServer.registerHandler('*.test.local', ['A'], (question) => ({
732
+ name: question.name,
733
+ type: 'A',
734
+ class: 'IN',
735
+ ttl: 60,
736
+ data: '127.0.0.2',
737
+ }));
738
+ });
739
+
740
+ tap.test('DNS Server - Query via UDP', async (tools) => {
741
+ const dnsPacket = (await import('dns-packet')).default;
742
+ const dgram = await import('dgram');
743
+
744
+ const query = dnsPacket.encode({
745
+ type: 'query',
746
+ id: 1234,
747
+ questions: [{
748
+ type: 'A',
749
+ class: 'IN',
750
+ name: 'test.local',
751
+ }],
370
752
  });
753
+
754
+ const client = dgram.createSocket('udp4');
755
+ const done = tools.defer();
756
+
757
+ client.on('message', (msg) => {
758
+ const response = dnsPacket.decode(msg);
759
+ expect(response.answers[0].data).toEqual('127.0.0.1');
760
+ client.close();
761
+ done.resolve();
762
+ });
763
+
764
+ client.send(query, 5353, 'localhost'); // Use the port specified during server creation
765
+ await done.promise;
371
766
  });
372
767
 
373
- tap.test('should stop the server', async () => {
768
+ tap.test('DNS Server - Cleanup', async () => {
374
769
  await dnsServer.stop();
375
- expect(dnsServer.isRunning()).toBeFalse();
376
770
  });
377
771
 
772
+ // Run tests
378
773
  await tap.start();
379
774
  ```
380
775
 
381
- The above tests ensure that the DNS server setup, query handling, and proper stopping of the server are all functioning as intended.
776
+ ### Best Practices
777
+
778
+ 1. **Port Selection**: Use non-privileged ports (>1024) during development
779
+ 2. **Handler Organization**: Group related handlers together
780
+ 3. **Error Handling**: Always handle DNS query errors gracefully
781
+ 4. **DNSSEC**: Enable DNSSEC for production deployments
782
+ 5. **Monitoring**: Log DNS queries for debugging and analytics
783
+ 6. **Rate Limiting**: Implement rate limiting for public DNS servers
784
+ 7. **Caching**: Respect TTL values and implement proper caching
785
+ 8. **Manual Sockets**: Use manual socket handling for clustering and load balancing
786
+
787
+ ### Performance Considerations
788
+
789
+ - The DNS client uses HTTP keep-alive for connection reuse
790
+ - The DNS server handles concurrent UDP and HTTPS requests efficiently
791
+ - DNSSEC signatures are generated on-demand to reduce memory usage
792
+ - Pattern matching uses caching for improved performance
793
+ - Manual socket handling enables horizontal scaling across CPU cores
794
+
795
+ ### Security Considerations
382
796
 
383
- In a realistic production environment, additional tests would include edge cases such as malformed requests, large queries, concurrent access handling, and integration tests with various DNS resolvers. These tests ensure robustness and reliability of DNS services provided by the server.
797
+ - Always use DNSSEC for authenticated responses
798
+ - Enable DoH for encrypted DNS queries
799
+ - Validate and sanitize all DNS inputs
800
+ - Implement access controls for DNS server handlers
801
+ - Use Let's Encrypt for automatic SSL certificate management
802
+ - Never expose internal network information through DNS
803
+ - Bind to specific interfaces in production environments
804
+ - Use manual socket handling for custom security layers
384
805
 
385
- This comprehensive guide demonstrates how to implement, manage, and test a DNS server using `@push.rocks/smartdns`, making it an ideal tool for developers tasked with handling DNS management and setup in TypeScript projects. The library supports the full scope of DNS operations needed for modern applications, from basic record query to full-scale DNS server operations with advanced security extensions.
806
+ This comprehensive library provides everything needed for both DNS client operations and running production-grade DNS servers with modern security features in TypeScript.
386
807
 
387
808
  ## License and Legal Information
388
809
 
389
- 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) file within this repository.
810
+ 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.
390
811
 
391
812
  **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.
392
813
 
@@ -401,4 +822,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
401
822
 
402
823
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
403
824
 
404
- 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.
825
+ 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.