@push.rocks/smartdns 7.0.2 → 7.1.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdns',
6
- version: '7.0.2',
6
+ version: '7.1.0',
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
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlMQUF5TDtDQUN2TSxDQUFBIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdns",
3
- "version": "7.0.2",
3
+ "version": "7.1.0",
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": {
package/readme.hints.md CHANGED
@@ -1 +1,97 @@
1
-
1
+ # smartdns - Implementation Hints
2
+
3
+ ## Architecture Overview
4
+
5
+ The smartdns library is structured into three main modules:
6
+
7
+ 1. **Client Module** (`ts_client/`) - DNS client functionality
8
+ 2. **Server Module** (`ts_server/`) - DNS server implementation
9
+ 3. **Main Module** (`ts/`) - Re-exports both client and server
10
+
11
+ ## Client Module (Smartdns class)
12
+
13
+ ### Key Features:
14
+ - DNS record queries (A, AAAA, TXT, MX, etc.)
15
+ - Support for multiple DNS providers (Google DNS, Cloudflare)
16
+ - DNS propagation checking with retry logic
17
+ - DNSSEC verification support
18
+ - Both HTTP-based (DoH) and Node.js DNS resolver fallback
19
+
20
+ ### Implementation Details:
21
+ - Uses Cloudflare's DNS-over-HTTPS API as primary resolver
22
+ - Falls back to Node.js DNS module for local resolution
23
+ - Implements automatic retry logic with configurable intervals
24
+ - Properly handles quoted TXT records and trailing dots in domain names
25
+
26
+ ### Key Methods:
27
+ - `getRecordsA()`, `getRecordsAAAA()`, `getRecordsTxt()` - Type-specific queries
28
+ - `getRecords()` - Generic record query with retry support
29
+ - `checkUntilAvailable()` - DNS propagation verification
30
+ - `getNameServers()` - NS record lookup
31
+ - `makeNodeProcessUseDnsProvider()` - Configure system DNS resolver
32
+
33
+ ## Server Module (DnsServer class)
34
+
35
+ ### Key Features:
36
+ - Full DNS server supporting UDP and HTTPS (DoH) protocols
37
+ - DNSSEC implementation with multiple algorithms
38
+ - Dynamic handler registration for custom responses
39
+ - Let's Encrypt integration for automatic SSL certificates
40
+ - Wildcard domain support with pattern matching
41
+
42
+ ### DNSSEC Implementation:
43
+ - Supports ECDSA (algorithm 13), ED25519 (algorithm 15), and RSA (algorithm 8)
44
+ - Automatic DNSKEY and DS record generation
45
+ - RRSIG signature generation for authenticated responses
46
+ - Key tag computation following RFC 4034
47
+
48
+ ### Let's Encrypt Integration:
49
+ - Automatic SSL certificate retrieval using DNS-01 challenges
50
+ - Dynamic TXT record handler registration for ACME validation
51
+ - Certificate renewal and HTTPS server restart capability
52
+ - Domain authorization filtering for security
53
+
54
+ ### Handler System:
55
+ - Pattern-based domain matching using minimatch
56
+ - Support for all common record types
57
+ - Handler chaining for complex scenarios
58
+ - Automatic SOA response for unhandled queries
59
+
60
+ ## Key Dependencies
61
+
62
+ - `dns-packet`: DNS packet encoding/decoding (wire format)
63
+ - `elliptic`: Cryptographic operations for DNSSEC
64
+ - `acme-client`: Let's Encrypt certificate automation
65
+ - `minimatch`: Glob pattern matching for domains
66
+ - `@push.rocks/smartrequest`: HTTP client for DoH queries
67
+ - `@tsclass/tsclass`: Type definitions for DNS records
68
+
69
+ ## Testing Insights
70
+
71
+ The test suite demonstrates:
72
+ - Mock ACME client for testing Let's Encrypt integration
73
+ - Self-signed certificate generation for HTTPS testing
74
+ - Unique port allocation to avoid conflicts
75
+ - Proper server cleanup between tests
76
+ - Both UDP and HTTPS query validation
77
+
78
+ ## Common Patterns
79
+
80
+ 1. **DNS Record Types**: Internally mapped to numeric values (A=1, AAAA=28, etc.)
81
+ 2. **Error Handling**: Graceful fallback and retry mechanisms
82
+ 3. **DNSSEC Workflow**: Zone → Key Generation → Signing → Verification
83
+ 4. **Certificate Flow**: Domain validation → Challenge setup → Verification → Certificate retrieval
84
+
85
+ ## Performance Considerations
86
+
87
+ - Client implements caching via DNS-over-HTTPS responses
88
+ - Server can handle concurrent UDP and HTTPS requests
89
+ - DNSSEC signing is performed on-demand for efficiency
90
+ - Handler registration is O(n) lookup but uses pattern caching
91
+
92
+ ## Security Notes
93
+
94
+ - DNSSEC provides authentication but not encryption
95
+ - DoH (DNS-over-HTTPS) provides both privacy and integrity
96
+ - Let's Encrypt integration requires proper domain authorization
97
+ - Handler patterns should be carefully designed to avoid open resolvers
package/readme.md CHANGED
@@ -1,392 +1,539 @@
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
84
+
85
+ The client supports various other DNS record types:
72
86
 
73
- ### Advanced DNS Management
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
+ ```
74
97
 
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.
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');
134
+
135
+ // Or use Google DNS
136
+ makeNodeProcessUseDnsProvider('google');
137
+ ```
98
138
 
99
- #### Example: Feature Flagging via TXT Records
139
+ ### Real-World Use Cases
100
140
 
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:
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({
131
- httpsKey: 'path/to/key.pem',
132
- httpsCert: 'path/to/cert.pem',
133
- httpsPort: 443,
134
- udpPort: 53,
135
- dnssecZone: 'example.com',
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
136
191
  });
137
192
 
193
+ // Register a handler for all subdomains of example.com
138
194
  dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
139
195
  name: question.name,
140
196
  type: 'A',
141
197
  class: 'IN',
142
198
  ttl: 300,
143
- data: '127.0.0.1',
199
+ data: '192.168.1.100',
144
200
  }));
145
201
 
146
- dnsServer.start().then(() => console.log('DNS Server started'));
147
- ```
202
+ // Register a handler for TXT records
203
+ dnsServer.registerHandler('example.com', ['TXT'], (question) => ({
204
+ name: question.name,
205
+ type: 'TXT',
206
+ class: 'IN',
207
+ ttl: 300,
208
+ data: 'v=spf1 include:_spf.example.com ~all',
209
+ }));
148
210
 
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.
211
+ // Start the server
212
+ await dnsServer.start();
213
+ console.log('DNS Server started!');
214
+ ```
150
215
 
151
216
  ### DNSSEC Support
152
217
 
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.
218
+ The DNS server includes comprehensive DNSSEC support with automatic key generation and record signing:
158
219
 
159
220
  ```typescript
160
- import { DnsServer } from '@push.rocks/smartdns';
221
+ import { DnsServer } from '@push.rocks/smartdns/server';
161
222
 
162
223
  const dnsServer = new DnsServer({
163
- httpsKey: 'path/to/key.pem',
164
- httpsCert: 'path/to/cert.pem',
165
- httpsPort: 443,
166
224
  udpPort: 53,
167
- dnssecZone: 'example.com',
225
+ httpsPort: 443,
226
+ dnssecZone: 'secure.example.com', // Enable DNSSEC for this zone
168
227
  });
169
228
 
170
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
229
+ // The server automatically:
230
+ // 1. Generates DNSKEY records with ECDSA (algorithm 13)
231
+ // 2. Creates DS records for parent zone delegation
232
+ // 3. Signs all responses with RRSIG records
233
+ // 4. Provides NSEC records for authenticated denial of existence
234
+
235
+ // Register your handlers as normal - DNSSEC signing is automatic
236
+ dnsServer.registerHandler('secure.example.com', ['A'], (question) => ({
171
237
  name: question.name,
172
238
  type: 'A',
173
239
  class: 'IN',
174
240
  ttl: 300,
175
- data: '127.0.0.1',
241
+ data: '192.168.1.1',
176
242
  }));
177
243
 
178
- dnsServer.start().then(() => console.log('DNS Server with DNSSEC started'));
244
+ await dnsServer.start();
245
+
246
+ // Query for DNSSEC records
247
+ import { Smartdns } from '@push.rocks/smartdns/client';
248
+ const client = new Smartdns({});
249
+ const dnskeyRecords = await client.getRecords('secure.example.com', 'DNSKEY');
250
+ const dsRecords = await client.getRecords('secure.example.com', 'DS');
179
251
  ```
180
252
 
181
- ### Handling DNS Queries Over Different Protocols
253
+ #### Supported DNSSEC Algorithms
182
254
 
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.
255
+ The server supports multiple DNSSEC algorithms:
256
+ - **ECDSAP256SHA256** (Algorithm 13) - Default, using P-256 curve
257
+ - **ED25519** (Algorithm 15) - Modern elliptic curve algorithm
258
+ - **RSASHA256** (Algorithm 8) - RSA-based signatures
184
259
 
185
- #### Handling UDP Queries
260
+ ### Let's Encrypt Integration
186
261
 
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:
262
+ The DNS server includes built-in Let's Encrypt support for automatic SSL certificate management:
188
263
 
189
264
  ```typescript
190
- import { DnsServer } from '@push.rocks/smartdns';
191
- import dgram from 'dgram';
265
+ import { DnsServer } from '@push.rocks/smartdns/server';
192
266
 
193
267
  const dnsServer = new DnsServer({
194
268
  udpPort: 53,
195
269
  httpsPort: 443,
270
+ httpsKey: '/path/to/letsencrypt/key.pem', // Will be auto-generated
271
+ httpsCert: '/path/to/letsencrypt/cert.pem', // Will be auto-generated
196
272
  });
197
273
 
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
- }));
205
-
206
- dnsServer.start().then(() => {
207
- console.log('UDP DNS Server started on port', dnsServer.getOptions().udpPort);
208
- });
274
+ // Retrieve Let's Encrypt certificate for your domain
275
+ const result = await dnsServer.retrieveSslCertificate(
276
+ ['secure.example.com', 'www.secure.example.com'],
277
+ {
278
+ email: 'admin@example.com',
279
+ staging: false, // Use production Let's Encrypt
280
+ certDir: './certs'
281
+ }
282
+ );
209
283
 
210
- const client = dgram.createSocket('udp4');
284
+ if (result.success) {
285
+ console.log('Certificate retrieved successfully!');
286
+ }
211
287
 
212
- client.on('message', (msg, rinfo) => {
213
- console.log(`Received ${msg} from ${rinfo.address}:${rinfo.port}`);
214
- });
288
+ // The server automatically:
289
+ // 1. Handles ACME DNS-01 challenges
290
+ // 2. Creates temporary TXT records for domain validation
291
+ // 3. Retrieves and installs the certificate
292
+ // 4. Restarts the HTTPS server with the new certificate
215
293
 
216
- client.send(Buffer.from('example DNS query'), dnsServer.getOptions().udpPort, 'localhost');
294
+ await dnsServer.start();
295
+ console.log('DNS Server with Let\'s Encrypt SSL started!');
217
296
  ```
218
297
 
219
- This segment of code creates a UDP server that listens for incoming DNS requests and responds accordingly.
298
+ ### Handling Different Protocols
220
299
 
221
- #### Handling HTTPS Queries
300
+ #### UDP DNS Server
222
301
 
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.
302
+ Traditional DNS queries over UDP (port 53):
224
303
 
225
304
  ```typescript
226
- import { DnsServer } from '@push.rocks/smartdns';
227
- import https from 'https';
228
- import fs from 'fs';
305
+ import { DnsServer } from '@push.rocks/smartdns/server';
306
+ import * as plugins from '@push.rocks/smartdns/server/plugins';
229
307
 
230
308
  const dnsServer = new DnsServer({
231
- httpsKey: fs.readFileSync('path/to/key.pem'),
232
- httpsCert: fs.readFileSync('path/to/cert.pem'),
233
- httpsPort: 443,
234
- udpPort: 53,
235
- dnssecZone: 'example.com',
309
+ udpPort: 5353, // Using alternate port for testing
310
+ httpsPort: 8443,
311
+ httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
312
+ httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
313
+ dnssecZone: 'test.local' // Optional
236
314
  });
237
315
 
238
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
316
+ // The UDP server automatically handles DNS packet parsing and encoding
317
+ dnsServer.registerHandler('test.local', ['A'], (question) => ({
239
318
  name: question.name,
240
- type: 'A',
319
+ type: 'A',
241
320
  class: 'IN',
242
- ttl: 300,
321
+ ttl: 60,
243
322
  data: '127.0.0.1',
244
323
  }));
245
324
 
246
- dnsServer.start().then(() => console.log('HTTPS DNS Server started'));
325
+ await dnsServer.start();
247
326
 
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
- });
327
+ // Test with dig or nslookup:
328
+ // dig @localhost -p 5353 test.local
329
+ ```
330
+
331
+ #### DNS-over-HTTPS (DoH) Server
332
+
333
+ Provide encrypted DNS queries over HTTPS:
334
+
335
+ ```typescript
336
+ import { DnsServer } from '@push.rocks/smartdns/server';
337
+ import * as fs from 'fs';
261
338
 
262
- client.on('error', (e) => {
263
- console.error(e);
339
+ const dnsServer = new DnsServer({
340
+ httpsPort: 8443,
341
+ httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
342
+ httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
264
343
  });
265
344
 
266
- client.write(Buffer.from('example DNS query'));
267
- client.end();
268
- ```
345
+ // The HTTPS server automatically handles:
346
+ // - DNS wire format in POST body
347
+ // - Proper Content-Type headers (application/dns-message)
348
+ // - Base64url encoding for GET requests
269
349
 
270
- This ensures that DNS requests can be securely transmitted over the web, maintaining privacy for the clients querying the DNS server.
350
+ dnsServer.registerHandler('secure.local', ['A'], (question) => ({
351
+ name: question.name,
352
+ type: 'A',
353
+ class: 'IN',
354
+ ttl: 300,
355
+ data: '10.0.0.1',
356
+ }));
271
357
 
272
- ### Testing
358
+ await dnsServer.start();
273
359
 
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.
360
+ // Test with curl:
361
+ // curl -H "Content-Type: application/dns-message" \
362
+ // --data-binary @query.bin \
363
+ // https://localhost:8443/dns-query
364
+ ```
275
365
 
276
- #### DNS Server Tests
366
+ ### Advanced Handler Patterns
277
367
 
278
- `@push.rocks/smartdns` integrates seamlessly with TAP, allowing for comprehensive testing of server functionalities.
368
+ #### Pattern-Based Routing
279
369
 
280
- Here's an example of how you might set up tests for your DNS server:
370
+ Use glob patterns for flexible domain matching:
281
371
 
282
372
  ```typescript
283
- import { expect, tap } from '@push.rocks/tapbundle';
373
+ // Match all subdomains
374
+ dnsServer.registerHandler('*.example.com', ['A'], (question) => {
375
+ // Extract subdomain
376
+ const subdomain = question.name.replace('.example.com', '');
377
+
378
+ // Dynamic response based on subdomain
379
+ return {
380
+ name: question.name,
381
+ type: 'A',
382
+ class: 'IN',
383
+ ttl: 300,
384
+ data: subdomain === 'api' ? '10.0.0.10' : '10.0.0.1',
385
+ };
386
+ });
387
+
388
+ // Match specific patterns
389
+ dnsServer.registerHandler('db-*.service.local', ['A'], (question) => {
390
+ const instanceId = question.name.match(/db-(\d+)/)?.[1];
391
+ return {
392
+ name: question.name,
393
+ type: 'A',
394
+ class: 'IN',
395
+ ttl: 60,
396
+ data: `10.0.1.${instanceId}`,
397
+ };
398
+ });
284
399
 
285
- import { DnsServer } from '@push.rocks/smartdns';
400
+ // Catch-all handler
401
+ dnsServer.registerHandler('*', ['A'], (question) => ({
402
+ name: question.name,
403
+ type: 'A',
404
+ class: 'IN',
405
+ ttl: 300,
406
+ data: '127.0.0.1',
407
+ }));
408
+ ```
409
+
410
+ ### Testing
286
411
 
412
+ The library uses `@git.zone/tstest` for testing. Here's an example of comprehensive tests:
413
+
414
+ ```typescript
415
+ import { expect, tap } from '@git.zone/tstest/tapbundle';
416
+ import { Smartdns } from '@push.rocks/smartdns/client';
417
+ import { DnsServer } from '@push.rocks/smartdns/server';
418
+
419
+ // Test DNS Client
420
+ tap.test('DNS Client - Query Records', async () => {
421
+ const dnsClient = new Smartdns({});
422
+
423
+ // Test A record query
424
+ const aRecords = await dnsClient.getRecordsA('google.com');
425
+ expect(aRecords).toBeArray();
426
+ expect(aRecords[0]).toHaveProperty('type', 'A');
427
+ expect(aRecords[0].data).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
428
+
429
+ // Test TXT record query
430
+ const txtRecords = await dnsClient.getRecordsTxt('google.com');
431
+ expect(txtRecords).toBeArray();
432
+ expect(txtRecords[0]).toHaveProperty('type', 'TXT');
433
+ });
434
+
435
+ // Test DNS Server
287
436
  let dnsServer: DnsServer;
288
437
 
289
- tap.test('should create an instance of DnsServer', async () => {
438
+ tap.test('DNS Server - Setup and Start', async () => {
290
439
  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',
440
+ udpPort: 5353,
441
+ httpsPort: 8443,
442
+ httpsKey: 'test-key', // Use test certificates
443
+ httpsCert: 'test-cert',
444
+ dnssecZone: 'test.local'
296
445
  });
446
+
297
447
  expect(dnsServer).toBeInstanceOf(DnsServer);
298
- });
299
-
300
- tap.test('should start the server', async () => {
301
448
  await dnsServer.start();
302
- expect(dnsServer.isRunning()).toBeTrue();
303
449
  });
304
450
 
305
- tap.test('should add a DNS handler', async () => {
306
- dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
307
- name: question.name,
451
+ tap.test('DNS Server - Register Handlers', async () => {
452
+ // Register multiple handlers
453
+ dnsServer.registerHandler('test.local', ['A'], () => ({
454
+ name: 'test.local',
308
455
  type: 'A',
309
456
  class: 'IN',
310
457
  ttl: 300,
311
458
  data: '127.0.0.1',
312
459
  }));
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
- });
327
-
328
- expect(response.answers[0]).toEqual({
329
- name: 'test.example.com',
330
- type: 'A',
460
+
461
+ dnsServer.registerHandler('*.test.local', ['A'], (question) => ({
462
+ name: question.name,
463
+ type: 'A',
331
464
  class: 'IN',
332
- ttl: 300,
333
- data: '127.0.0.1',
334
- });
465
+ ttl: 60,
466
+ data: '127.0.0.2',
467
+ }));
335
468
  });
336
469
 
337
- tap.test('should query the server over HTTP', async () => {
470
+ tap.test('DNS Server - Query via UDP', async (tools) => {
471
+ const dnsPacket = (await import('dns-packet')).default;
472
+ const dgram = await import('dgram');
473
+
338
474
  const query = dnsPacket.encode({
339
475
  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
- ],
476
+ id: 1234,
477
+ questions: [{
478
+ type: 'A',
479
+ class: 'IN',
480
+ name: 'test.local',
481
+ }],
349
482
  });
350
-
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
- });
358
-
359
- expect(response.status).toEqual(200);
360
-
361
- const responseData = await response.arrayBuffer();
362
- const dnsResponse = dnsPacket.decode(Buffer.from(responseData));
363
-
364
- expect(dnsResponse.answers[0]).toEqual({
365
- name: 'test.example.com',
366
- type: 'A',
367
- class: 'IN',
368
- ttl: 300,
369
- data: '127.0.0.1',
483
+
484
+ const client = dgram.createSocket('udp4');
485
+ const done = tools.defer();
486
+
487
+ client.on('message', (msg) => {
488
+ const response = dnsPacket.decode(msg);
489
+ expect(response.answers[0].data).toEqual('127.0.0.1');
490
+ client.close();
491
+ done.resolve();
370
492
  });
493
+
494
+ client.send(query, 5353, 'localhost'); // Use the port specified during server creation
495
+ await done.promise;
371
496
  });
372
497
 
373
- tap.test('should stop the server', async () => {
498
+ tap.test('DNS Server - Cleanup', async () => {
374
499
  await dnsServer.stop();
375
- expect(dnsServer.isRunning()).toBeFalse();
376
500
  });
377
501
 
502
+ // Run tests
378
503
  await tap.start();
379
504
  ```
380
505
 
381
- The above tests ensure that the DNS server setup, query handling, and proper stopping of the server are all functioning as intended.
506
+ ### Best Practices
507
+
508
+ 1. **Port Selection**: Use non-privileged ports (>1024) during development
509
+ 2. **Handler Organization**: Group related handlers together
510
+ 3. **Error Handling**: Always handle DNS query errors gracefully
511
+ 4. **DNSSEC**: Enable DNSSEC for production deployments
512
+ 5. **Monitoring**: Log DNS queries for debugging and analytics
513
+ 6. **Rate Limiting**: Implement rate limiting for public DNS servers
514
+ 7. **Caching**: Respect TTL values and implement proper caching
515
+
516
+ ### Performance Considerations
517
+
518
+ - The DNS client uses HTTP keep-alive for connection reuse
519
+ - The DNS server handles concurrent UDP and HTTPS requests efficiently
520
+ - DNSSEC signatures are generated on-demand to reduce memory usage
521
+ - Pattern matching uses caching for improved performance
522
+
523
+ ### Security Considerations
382
524
 
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.
525
+ - Always use DNSSEC for authenticated responses
526
+ - Enable DoH for encrypted DNS queries
527
+ - Validate and sanitize all DNS inputs
528
+ - Implement access controls for DNS server handlers
529
+ - Use Let's Encrypt for automatic SSL certificate management
530
+ - Never expose internal network information through DNS
384
531
 
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.
532
+ This comprehensive library provides everything needed for both DNS client operations and running production-grade DNS servers with modern security features in TypeScript.
386
533
 
387
534
  ## License and Legal Information
388
535
 
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.
536
+ 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
537
 
391
538
  **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
539
 
@@ -401,4 +548,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
401
548
 
402
549
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
403
550
 
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.
551
+ 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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdns',
6
- version: '7.0.2',
6
+ version: '7.1.0',
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
  }