@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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/package.json +1 -1
- package/readme.hints.md +97 -1
- package/readme.md +354 -207
- package/ts/00_commitinfo_data.ts +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartdns',
|
|
6
|
-
version: '7.0
|
|
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
|
|
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
|
|
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
|
|
7
|
+
To install `@push.rocks/smartdns`, use the following command with pnpm:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
pnpm install @push.rocks/smartdns --save
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Or with
|
|
13
|
+
Or with npm:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
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
|
|
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
|
-
|
|
31
|
+
You can import the modules based on your needs:
|
|
28
32
|
|
|
29
33
|
```typescript
|
|
30
|
-
|
|
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
|
-
###
|
|
46
|
+
### DNS Client Operations
|
|
34
47
|
|
|
35
|
-
|
|
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
|
|
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
|
|
45
|
-
const aRecords = await
|
|
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
|
|
65
|
+
For resolving a domain to IPv6 addresses:
|
|
54
66
|
|
|
55
67
|
```typescript
|
|
56
|
-
const aaaaRecords = await
|
|
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
|
|
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
|
|
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
|
-
|
|
83
|
+
#### Other Record Types
|
|
84
|
+
|
|
85
|
+
The client supports various other DNS record types:
|
|
72
86
|
|
|
73
|
-
|
|
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
|
-
|
|
98
|
+
### Advanced DNS Features
|
|
76
99
|
|
|
77
100
|
#### Checking DNS Propagation
|
|
78
101
|
|
|
79
|
-
|
|
102
|
+
The client provides a powerful method to verify DNS propagation globally, essential when making DNS changes:
|
|
80
103
|
|
|
81
104
|
```typescript
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
const
|
|
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('
|
|
118
|
+
console.log('DNS record has propagated successfully!');
|
|
88
119
|
} else {
|
|
89
|
-
console.log('
|
|
120
|
+
console.log('DNS propagation timeout - record not found.');
|
|
90
121
|
}
|
|
91
122
|
```
|
|
92
123
|
|
|
93
|
-
|
|
124
|
+
#### Configuring System DNS Provider
|
|
94
125
|
|
|
95
|
-
|
|
126
|
+
You can configure Node.js to use a specific DNS provider for all DNS queries:
|
|
96
127
|
|
|
97
|
-
|
|
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
|
-
|
|
139
|
+
### Real-World Use Cases
|
|
100
140
|
|
|
101
|
-
|
|
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
|
|
105
|
-
const featureFlags =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
178
|
+
#### Basic DNS Server Setup
|
|
124
179
|
|
|
125
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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: '
|
|
199
|
+
data: '192.168.1.100',
|
|
144
200
|
}));
|
|
145
201
|
|
|
146
|
-
|
|
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
|
-
|
|
211
|
+
// Start the server
|
|
212
|
+
await dnsServer.start();
|
|
213
|
+
console.log('DNS Server started!');
|
|
214
|
+
```
|
|
150
215
|
|
|
151
216
|
### DNSSEC Support
|
|
152
217
|
|
|
153
|
-
|
|
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
|
-
|
|
225
|
+
httpsPort: 443,
|
|
226
|
+
dnssecZone: 'secure.example.com', // Enable DNSSEC for this zone
|
|
168
227
|
});
|
|
169
228
|
|
|
170
|
-
|
|
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: '
|
|
241
|
+
data: '192.168.1.1',
|
|
176
242
|
}));
|
|
177
243
|
|
|
178
|
-
dnsServer.start()
|
|
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
|
-
|
|
253
|
+
#### Supported DNSSEC Algorithms
|
|
182
254
|
|
|
183
|
-
The
|
|
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
|
-
|
|
260
|
+
### Let's Encrypt Integration
|
|
186
261
|
|
|
187
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
284
|
+
if (result.success) {
|
|
285
|
+
console.log('Certificate retrieved successfully!');
|
|
286
|
+
}
|
|
211
287
|
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
294
|
+
await dnsServer.start();
|
|
295
|
+
console.log('DNS Server with Let\'s Encrypt SSL started!');
|
|
217
296
|
```
|
|
218
297
|
|
|
219
|
-
|
|
298
|
+
### Handling Different Protocols
|
|
220
299
|
|
|
221
|
-
####
|
|
300
|
+
#### UDP DNS Server
|
|
222
301
|
|
|
223
|
-
|
|
302
|
+
Traditional DNS queries over UDP (port 53):
|
|
224
303
|
|
|
225
304
|
```typescript
|
|
226
|
-
import { DnsServer } from '@push.rocks/smartdns';
|
|
227
|
-
import
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
dnssecZone: '
|
|
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
|
-
|
|
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:
|
|
321
|
+
ttl: 60,
|
|
243
322
|
data: '127.0.0.1',
|
|
244
323
|
}));
|
|
245
324
|
|
|
246
|
-
dnsServer.start()
|
|
325
|
+
await dnsServer.start();
|
|
247
326
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
267
|
-
|
|
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
|
-
|
|
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
|
-
|
|
358
|
+
await dnsServer.start();
|
|
273
359
|
|
|
274
|
-
|
|
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
|
-
|
|
366
|
+
### Advanced Handler Patterns
|
|
277
367
|
|
|
278
|
-
|
|
368
|
+
#### Pattern-Based Routing
|
|
279
369
|
|
|
280
|
-
|
|
370
|
+
Use glob patterns for flexible domain matching:
|
|
281
371
|
|
|
282
372
|
```typescript
|
|
283
|
-
|
|
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
|
-
|
|
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('
|
|
438
|
+
tap.test('DNS Server - Setup and Start', async () => {
|
|
290
439
|
dnsServer = new DnsServer({
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
dnssecZone: '
|
|
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('
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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:
|
|
333
|
-
data: '127.0.0.
|
|
334
|
-
});
|
|
465
|
+
ttl: 60,
|
|
466
|
+
data: '127.0.0.2',
|
|
467
|
+
}));
|
|
335
468
|
});
|
|
336
469
|
|
|
337
|
-
tap.test('
|
|
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:
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartdns',
|
|
6
|
-
version: '7.0
|
|
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
|
}
|