yq-dns 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +265 -0
- package/README.md +1026 -0
- package/dist/index.cjs +8 -0
- package/dist/index.js +8 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,1026 @@
|
|
|
1
|
+
# yq-dns
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/yq-dns)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](http://www.typescriptlang.org/)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
|
|
8
|
+
A comprehensive, promise-based DNS resolver library that aggregates multiple DNS-over-HTTPS (DoH) providers with advanced concurrency control, email validation, and flexible configuration. Built with TypeScript for maximum type safety and developer experience.
|
|
9
|
+
|
|
10
|
+
## 📋 Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Features](#-features)
|
|
13
|
+
- [Installation](#-installation)
|
|
14
|
+
- [Quick Start](#-quick-start)
|
|
15
|
+
- [ESM (ES Modules)](#esm-es-modules)
|
|
16
|
+
- [CommonJS](#commonjs)
|
|
17
|
+
- [DNS Providers](#-dns-providers)
|
|
18
|
+
- [Configuration](#-configuration)
|
|
19
|
+
- [Provider Configuration](#provider-configuration)
|
|
20
|
+
- [Global Configuration](#global-configuration)
|
|
21
|
+
- [Default Settings](#default-settings)
|
|
22
|
+
- [API Reference](#-api-reference)
|
|
23
|
+
- [YqDns Class](#yqdns-class)
|
|
24
|
+
- [Standalone Functions](#standalone-functions)
|
|
25
|
+
- [DNS Resolution Methods](#dns-resolution-methods)
|
|
26
|
+
- [Email Validation](#email-validation)
|
|
27
|
+
- [DNS Record Types](#-dns-record-types)
|
|
28
|
+
- [Advanced Usage](#-advanced-usage)
|
|
29
|
+
- [Rate Limiting](#rate-limiting)
|
|
30
|
+
- [Provider Selection](#provider-selection)
|
|
31
|
+
- [Error Handling](#error-handling)
|
|
32
|
+
- [Concurrency Control](#concurrency-control)
|
|
33
|
+
- [Email Validation System](#-email-validation-system)
|
|
34
|
+
- [Domain Validation](#domain-validation)
|
|
35
|
+
- [Webmail Detection](#webmail-detection)
|
|
36
|
+
- [Provider Recognition](#provider-recognition)
|
|
37
|
+
- [TypeScript Support](#-typescript-support)
|
|
38
|
+
- [Performance](#-performance)
|
|
39
|
+
- [Examples](#-examples)
|
|
40
|
+
- [Troubleshooting](#-troubleshooting)
|
|
41
|
+
- [Contributing](#-contributing)
|
|
42
|
+
- [License](#-license)
|
|
43
|
+
|
|
44
|
+
## 🚀 Features
|
|
45
|
+
|
|
46
|
+
- **🌐 Multiple DNS Providers**: Google DoH, Cloudflare DoH, Quad9 DoH, and Node.js native DNS
|
|
47
|
+
- **⚡ High Performance**: Concurrent requests with intelligent load balancing
|
|
48
|
+
- **🎯 Smart Provider Selection**: Automatic failover and random selection
|
|
49
|
+
- **🔧 Flexible Configuration**: Per-provider concurrency and rate limiting
|
|
50
|
+
- **📝 Full TypeScript Support**: Complete type definitions and IntelliSense
|
|
51
|
+
- **🌍 Cross-Platform**: Works with Node.js 18+, Deno, and Bun
|
|
52
|
+
- **📊 Complete DNS API**: All standard DNS record types supported
|
|
53
|
+
- **📧 Email Validation**: Comprehensive email domain validation and webmail detection
|
|
54
|
+
- **🛡️ Error Handling**: Robust error handling with detailed error information
|
|
55
|
+
- **🔄 Automatic Retry**: Built-in retry logic with exponential backoff
|
|
56
|
+
- **📈 Queue Management**: Advanced queue management with rate limiter
|
|
57
|
+
- **🎛️ Runtime Configuration**: Dynamic configuration updates without restart
|
|
58
|
+
|
|
59
|
+
## 📦 Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install yq-dns
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yarn add yq-dns
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm add yq-dns
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 🚀 Quick Start
|
|
74
|
+
|
|
75
|
+
### ESM (ES Modules)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Using standalone functions (recommended for simple use cases)
|
|
79
|
+
import { resolve4, resolveMx, resolveAny } from 'yq-dns';
|
|
80
|
+
|
|
81
|
+
// Resolve A records
|
|
82
|
+
const addresses = await resolve4('example.com');
|
|
83
|
+
console.log(addresses); // ['93.184.216.34']
|
|
84
|
+
|
|
85
|
+
// Resolve MX records
|
|
86
|
+
const mxRecords = await resolveMx('example.com');
|
|
87
|
+
console.log(mxRecords); // [{ priority: 10, exchange: 'mail.example.com' }]
|
|
88
|
+
|
|
89
|
+
// Resolve with specific provider
|
|
90
|
+
const googleResult = await resolve4('example.com', undefined, 'google');
|
|
91
|
+
|
|
92
|
+
// Resolve with TTL information
|
|
93
|
+
const withTtl = await resolve4('example.com', { ttl: true });
|
|
94
|
+
console.log(withTtl); // [{ address: '93.184.216.34', ttl: 300 }]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Using YqDns class (recommended for advanced configuration)
|
|
99
|
+
import { YqDns } from 'yq-dns';
|
|
100
|
+
|
|
101
|
+
const dns = new YqDns({
|
|
102
|
+
google: { enabled: true, limit: { concurrency: 10 } },
|
|
103
|
+
cloudflare: { enabled: true, limit: { concurrency: 5 } },
|
|
104
|
+
quad9: { enabled: false },
|
|
105
|
+
native: { enabled: true }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const addresses = await dns.resolve4('example.com');
|
|
109
|
+
const mxRecords = await dns.resolveMx('example.com');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### CommonJS
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Using require (Node.js with CommonJS)
|
|
116
|
+
const { resolve4, resolveMx, YqDns } = require('yq-dns');
|
|
117
|
+
|
|
118
|
+
// Standalone functions
|
|
119
|
+
resolve4('example.com').then(addresses => {
|
|
120
|
+
console.log(addresses);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Class usage
|
|
124
|
+
const dns = new YqDns({
|
|
125
|
+
google: { enabled: true },
|
|
126
|
+
cloudflare: { enabled: true }
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
dns.resolve4('example.com').then(addresses => {
|
|
130
|
+
console.log(addresses);
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 🌐 DNS Providers
|
|
135
|
+
|
|
136
|
+
### Native Provider
|
|
137
|
+
- **Endpoint**: Node.js built-in `dns/promises` module
|
|
138
|
+
- **Features**: Supports all DNS methods including `lookup()`
|
|
139
|
+
- **Use Case**: Local/internal DNS resolution, system DNS settings
|
|
140
|
+
- **Advantages**: No external dependencies, respects system DNS configuration
|
|
141
|
+
|
|
142
|
+
### Google DoH
|
|
143
|
+
- **Endpoint**: `https://dns.google/resolve`
|
|
144
|
+
- **Features**: DNSSEC validation, high reliability
|
|
145
|
+
- **Use Case**: Public DNS resolution with Google's infrastructure
|
|
146
|
+
- **Advantages**: Global CDN, excellent uptime, DNSSEC support
|
|
147
|
+
|
|
148
|
+
### Cloudflare DoH
|
|
149
|
+
- **Endpoint**: `https://cloudflare-dns.com/dns-query`
|
|
150
|
+
- **Features**: Privacy-focused, minimal logging
|
|
151
|
+
- **Use Case**: Privacy-conscious applications
|
|
152
|
+
- **Advantages**: Fast global network, strong privacy policy
|
|
153
|
+
|
|
154
|
+
### Quad9 DoH
|
|
155
|
+
- **Endpoint**: `https://dns.quad9.net/dns-query`
|
|
156
|
+
- **Features**: Security-focused, threat blocking
|
|
157
|
+
- **Use Case**: Security-sensitive applications
|
|
158
|
+
- **Advantages**: Malware blocking, non-profit organization
|
|
159
|
+
|
|
160
|
+
## ⚙️ Configuration
|
|
161
|
+
|
|
162
|
+
### Provider Configuration
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface ProviderConfig {
|
|
166
|
+
enabled?: boolean; // Enable/disable provider (default: true)
|
|
167
|
+
limit?: {
|
|
168
|
+
concurrency: number; // Max concurrent requests
|
|
169
|
+
interval?: number; // Rate limiting interval in ms
|
|
170
|
+
intervalCap?: number; // Max requests per interval
|
|
171
|
+
carryoverConcurrencyCount?: boolean; // Carry over concurrency count
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Global Configuration
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
interface YqConfig {
|
|
180
|
+
native?: ProviderConfig; // Node.js built-in DNS
|
|
181
|
+
google?: ProviderConfig; // Google DoH
|
|
182
|
+
cloudflare?: ProviderConfig; // Cloudflare DoH
|
|
183
|
+
quad9?: ProviderConfig; // Quad9 DoH
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Default Settings
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const defaultConfig: YqConfig = {
|
|
191
|
+
native: {
|
|
192
|
+
enabled: true,
|
|
193
|
+
limit: {
|
|
194
|
+
concurrency: 10,
|
|
195
|
+
interval: 1000,
|
|
196
|
+
intervalCap: 200,
|
|
197
|
+
carryoverConcurrencyCount: true
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
google: {
|
|
201
|
+
enabled: true,
|
|
202
|
+
limit: {
|
|
203
|
+
concurrency: 5,
|
|
204
|
+
interval: 1000,
|
|
205
|
+
intervalCap: 50,
|
|
206
|
+
carryoverConcurrencyCount: true
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
cloudflare: {
|
|
210
|
+
enabled: true,
|
|
211
|
+
limit: {
|
|
212
|
+
concurrency: 7,
|
|
213
|
+
interval: 1000,
|
|
214
|
+
intervalCap: 70,
|
|
215
|
+
carryoverConcurrencyCount: true
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
quad9: {
|
|
219
|
+
enabled: true,
|
|
220
|
+
limit: {
|
|
221
|
+
concurrency: 5,
|
|
222
|
+
interval: 1000,
|
|
223
|
+
intervalCap: 50,
|
|
224
|
+
carryoverConcurrencyCount: true
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## 📚 API Reference
|
|
231
|
+
|
|
232
|
+
### YqDns Class
|
|
233
|
+
|
|
234
|
+
#### Constructor
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
new YqDns(config?: YqConfig)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Creates a new DNS resolver instance with optional configuration.
|
|
241
|
+
|
|
242
|
+
**Parameters:**
|
|
243
|
+
- `config` (optional): Global configuration for all providers
|
|
244
|
+
|
|
245
|
+
**Example:**
|
|
246
|
+
```typescript
|
|
247
|
+
const dns = new YqDns({
|
|
248
|
+
google: { enabled: true, limit: { concurrency: 15 } },
|
|
249
|
+
cloudflare: { enabled: false }
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Configuration Methods
|
|
254
|
+
|
|
255
|
+
##### `setConfig(provider: string, config: ProviderConfig): void`
|
|
256
|
+
|
|
257
|
+
Sets configuration for a specific provider.
|
|
258
|
+
|
|
259
|
+
**Parameters:**
|
|
260
|
+
- `provider`: Provider name ('native', 'google', 'cloudflare', 'quad9')
|
|
261
|
+
- `config`: Provider configuration object
|
|
262
|
+
|
|
263
|
+
**Example:**
|
|
264
|
+
```typescript
|
|
265
|
+
dns.setConfig('google', {
|
|
266
|
+
enabled: true,
|
|
267
|
+
limit: { concurrency: 20, interval: 1000, intervalCap: 100 }
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
##### `setConfigs(config: YqConfig): void`
|
|
272
|
+
|
|
273
|
+
Sets configurations for multiple providers at once.
|
|
274
|
+
|
|
275
|
+
**Parameters:**
|
|
276
|
+
- `config`: Global configuration object
|
|
277
|
+
|
|
278
|
+
**Example:**
|
|
279
|
+
```typescript
|
|
280
|
+
dns.setConfigs({
|
|
281
|
+
google: { enabled: true, limit: { concurrency: 10 } },
|
|
282
|
+
cloudflare: { enabled: false },
|
|
283
|
+
native: { enabled: true }
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Static Methods
|
|
288
|
+
|
|
289
|
+
##### `YqDns.createEmailValidator(config?: ValidationConfig, dnsConfig?: YqConfig): EmailValidator`
|
|
290
|
+
|
|
291
|
+
Creates or returns the singleton email validator instance.
|
|
292
|
+
|
|
293
|
+
**Parameters:**
|
|
294
|
+
- `config` (optional): Email validation configuration
|
|
295
|
+
- `dnsConfig` (optional): DNS configuration for the validator
|
|
296
|
+
|
|
297
|
+
**Returns:** EmailValidator instance
|
|
298
|
+
|
|
299
|
+
##### `YqDns.emailValidator: EmailValidator`
|
|
300
|
+
|
|
301
|
+
Gets the singleton email validator instance (creates one if it doesn't exist).
|
|
302
|
+
|
|
303
|
+
### Standalone Functions
|
|
304
|
+
|
|
305
|
+
All standalone functions use a default YqDns instance and support the same parameters as their class counterparts.
|
|
306
|
+
|
|
307
|
+
#### `resolve4(hostname: string, options?: ResolveOptions, provider?: ProviderName): Promise<string[] | RecordWithTtl[]>`
|
|
308
|
+
|
|
309
|
+
Resolves A (IPv4) records for a hostname.
|
|
310
|
+
|
|
311
|
+
**Parameters:**
|
|
312
|
+
- `hostname`: The hostname to resolve
|
|
313
|
+
- `options` (optional): Resolution options (`{ ttl: boolean }`)
|
|
314
|
+
- `provider` (optional): Specific provider to use
|
|
315
|
+
|
|
316
|
+
**Returns:** Array of IPv4 addresses or records with TTL
|
|
317
|
+
|
|
318
|
+
**Example:**
|
|
319
|
+
```typescript
|
|
320
|
+
// Basic usage
|
|
321
|
+
const addresses = await resolve4('example.com');
|
|
322
|
+
|
|
323
|
+
// With TTL
|
|
324
|
+
const withTtl = await resolve4('example.com', { ttl: true });
|
|
325
|
+
|
|
326
|
+
// With specific provider
|
|
327
|
+
const googleResult = await resolve4('example.com', undefined, 'google');
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### `resolve6(hostname: string, options?: ResolveOptions, provider?: ProviderName): Promise<string[] | RecordWithTtl[]>`
|
|
331
|
+
|
|
332
|
+
Resolves AAAA (IPv6) records for a hostname.
|
|
333
|
+
|
|
334
|
+
#### `resolveAny(hostname: string, provider?: ProviderName): Promise<AnyRecord[]>`
|
|
335
|
+
|
|
336
|
+
Resolves ANY records for a hostname.
|
|
337
|
+
|
|
338
|
+
#### `resolveCaa(hostname: string, provider?: ProviderName): Promise<CaaRecord[]>`
|
|
339
|
+
|
|
340
|
+
Resolves CAA (Certificate Authority Authorization) records.
|
|
341
|
+
|
|
342
|
+
#### `resolveCname(hostname: string, provider?: ProviderName): Promise<string[]>`
|
|
343
|
+
|
|
344
|
+
Resolves CNAME (Canonical Name) records.
|
|
345
|
+
|
|
346
|
+
#### `resolveMx(hostname: string, provider?: ProviderName): Promise<MxRecord[]>`
|
|
347
|
+
|
|
348
|
+
Resolves MX (Mail Exchange) records.
|
|
349
|
+
|
|
350
|
+
#### `resolveNaptr(hostname: string, provider?: ProviderName): Promise<NaptrRecord[]>`
|
|
351
|
+
|
|
352
|
+
Resolves NAPTR (Name Authority Pointer) records.
|
|
353
|
+
|
|
354
|
+
#### `resolveNs(hostname: string, provider?: ProviderName): Promise<string[]>`
|
|
355
|
+
|
|
356
|
+
Resolves NS (Name Server) records.
|
|
357
|
+
|
|
358
|
+
#### `resolvePtr(ip: string, provider?: ProviderName): Promise<string[]>`
|
|
359
|
+
|
|
360
|
+
Resolves PTR (Pointer) records for reverse DNS lookup.
|
|
361
|
+
|
|
362
|
+
#### `resolveSoa(hostname: string, provider?: ProviderName): Promise<SoaRecord>`
|
|
363
|
+
|
|
364
|
+
Resolves SOA (Start of Authority) record.
|
|
365
|
+
|
|
366
|
+
#### `resolveSrv(hostname: string, provider?: ProviderName): Promise<SrvRecord[]>`
|
|
367
|
+
|
|
368
|
+
Resolves SRV (Service) records.
|
|
369
|
+
|
|
370
|
+
#### `resolveTlsa(hostname: string, provider?: ProviderName): Promise<TlsaRecord[]>`
|
|
371
|
+
|
|
372
|
+
Resolves TLSA (Transport Layer Security Authentication) records.
|
|
373
|
+
|
|
374
|
+
#### `resolveTxt(hostname: string, provider?: ProviderName): Promise<string[][]>`
|
|
375
|
+
|
|
376
|
+
Resolves TXT records.
|
|
377
|
+
|
|
378
|
+
#### `reverse(ip: string, provider?: ProviderName): Promise<string[]>`
|
|
379
|
+
|
|
380
|
+
Performs reverse DNS lookup.
|
|
381
|
+
|
|
382
|
+
### DNS Resolution Methods
|
|
383
|
+
|
|
384
|
+
All DNS resolution methods in the YqDns class follow the same pattern:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// Method signature pattern
|
|
388
|
+
async methodName(hostname: string, options?: ResolveOptions, provider?: ProviderName): Promise<ReturnType>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Common Parameters:**
|
|
392
|
+
- `hostname`: The hostname or IP address to resolve
|
|
393
|
+
- `options` (optional): Resolution options (currently supports `{ ttl: boolean }`)
|
|
394
|
+
- `provider` (optional): Force using a specific provider ('native', 'google', 'cloudflare', 'quad9')
|
|
395
|
+
|
|
396
|
+
**Provider Selection Logic:**
|
|
397
|
+
1. If `provider` is specified, use that provider (must be enabled)
|
|
398
|
+
2. Otherwise, randomly select from enabled providers
|
|
399
|
+
3. Prefer providers that are not at queue capacity
|
|
400
|
+
4. Throw error if no providers are enabled
|
|
401
|
+
|
|
402
|
+
### Email Validation
|
|
403
|
+
|
|
404
|
+
#### `EmailValidator` Class
|
|
405
|
+
|
|
406
|
+
The EmailValidator provides comprehensive email validation and analysis.
|
|
407
|
+
|
|
408
|
+
##### `validate(email: string, deep?: boolean): Promise<EmailResponse>`
|
|
409
|
+
|
|
410
|
+
Validates an email address and determines its provider/service.
|
|
411
|
+
|
|
412
|
+
**Parameters:**
|
|
413
|
+
- `email`: Email address to validate
|
|
414
|
+
- `deep` (optional): Enable deep validation with additional DNS lookups (default: false)
|
|
415
|
+
|
|
416
|
+
**Returns:** EmailResponse object with one of the following structures:
|
|
417
|
+
|
|
418
|
+
**Success Response:**
|
|
419
|
+
```typescript
|
|
420
|
+
{
|
|
421
|
+
success: true,
|
|
422
|
+
data: {
|
|
423
|
+
provider: string, // Email provider name
|
|
424
|
+
email: string, // Normalized email address
|
|
425
|
+
role: boolean, // Whether it's a role-based email
|
|
426
|
+
webmail?: string // Webmail URL if available
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Error Response:**
|
|
432
|
+
```typescript
|
|
433
|
+
{
|
|
434
|
+
success: false,
|
|
435
|
+
type: 'Syntanx' | 'Rejected' | 'Invalid' | 'Error',
|
|
436
|
+
email: string,
|
|
437
|
+
message?: string // Only present for 'Error' type
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Example:**
|
|
442
|
+
```typescript
|
|
443
|
+
const validator = YqDns.emailValidator;
|
|
444
|
+
const result = await validator.validate('user@gmail.com');
|
|
445
|
+
if (result.success) {
|
|
446
|
+
console.log('Provider:', result.data.provider);
|
|
447
|
+
console.log('Webmail:', result.data.webmail);
|
|
448
|
+
console.log('Role email:', result.data.role);
|
|
449
|
+
} else {
|
|
450
|
+
console.log('Validation failed:', result.type);
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## 🗂️ DNS Record Types
|
|
455
|
+
|
|
456
|
+
### Supported Record Types
|
|
457
|
+
|
|
458
|
+
| Record Type | Description | Return Type |
|
|
459
|
+
|-------------|-------------|-------------|
|
|
460
|
+
| **A** | IPv4 address records | `string[]` or `RecordWithTtl[]` |
|
|
461
|
+
| **AAAA** | IPv6 address records | `string[]` or `RecordWithTtl[]` |
|
|
462
|
+
| **ANY** | All available records | `AnyRecord[]` |
|
|
463
|
+
| **CAA** | Certificate Authority Authorization | `CaaRecord[]` |
|
|
464
|
+
| **CNAME** | Canonical name records | `string[]` |
|
|
465
|
+
| **MX** | Mail exchange records | `MxRecord[]` |
|
|
466
|
+
| **NAPTR** | Name Authority Pointer | `NaptrRecord[]` |
|
|
467
|
+
| **NS** | Name server records | `string[]` |
|
|
468
|
+
| **PTR** | Pointer records (reverse DNS) | `string[]` |
|
|
469
|
+
| **SOA** | Start of Authority | `SoaRecord` |
|
|
470
|
+
| **SRV** | Service records | `SrvRecord[]` |
|
|
471
|
+
| **TLSA** | TLS Authentication | `TlsaRecord[]` |
|
|
472
|
+
| **TXT** | Text records | `string[][]` |
|
|
473
|
+
|
|
474
|
+
### Type Definitions
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
interface RecordWithTtl {
|
|
478
|
+
address: string;
|
|
479
|
+
ttl: number;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
interface MxRecord {
|
|
483
|
+
priority: number;
|
|
484
|
+
exchange: string;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
interface SrvRecord {
|
|
488
|
+
priority: number;
|
|
489
|
+
weight: number;
|
|
490
|
+
port: number;
|
|
491
|
+
name: string;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
interface SoaRecord {
|
|
495
|
+
nsname: string;
|
|
496
|
+
hostmaster: string;
|
|
497
|
+
serial: number;
|
|
498
|
+
refresh: number;
|
|
499
|
+
retry: number;
|
|
500
|
+
expire: number;
|
|
501
|
+
minttl: number;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
interface CaaRecord {
|
|
505
|
+
critical: number;
|
|
506
|
+
issue?: string;
|
|
507
|
+
issuewild?: string;
|
|
508
|
+
iodef?: string;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
interface NaptrRecord {
|
|
512
|
+
flags: string;
|
|
513
|
+
service: string;
|
|
514
|
+
regexp: string;
|
|
515
|
+
replacement: string;
|
|
516
|
+
order: number;
|
|
517
|
+
preference: number;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
interface TlsaRecord {
|
|
521
|
+
usage: number;
|
|
522
|
+
selector: number;
|
|
523
|
+
matchingType: number;
|
|
524
|
+
certificate: string;
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## 🔧 Advanced Usage
|
|
529
|
+
|
|
530
|
+
### Rate Limiting
|
|
531
|
+
|
|
532
|
+
Configure rate limiting to control request frequency:
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
const dns = new YqDns({
|
|
536
|
+
google: {
|
|
537
|
+
enabled: true,
|
|
538
|
+
limit: {
|
|
539
|
+
concurrency: 5, // Max 5 concurrent requests
|
|
540
|
+
interval: 1000, // Per 1 second
|
|
541
|
+
intervalCap: 10, // Max 10 requests per interval
|
|
542
|
+
carryoverConcurrencyCount: true
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Provider Selection
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// Force specific provider
|
|
552
|
+
const googleResult = await resolve4('example.com', undefined, 'google');
|
|
553
|
+
const cloudflareResult = await resolve4('example.com', undefined, 'cloudflare');
|
|
554
|
+
|
|
555
|
+
// Let the library choose (recommended)
|
|
556
|
+
const autoResult = await resolve4('example.com');
|
|
557
|
+
|
|
558
|
+
// Disable specific providers
|
|
559
|
+
const dns = new YqDns({
|
|
560
|
+
quad9: { enabled: false }, // Disable Quad9
|
|
561
|
+
native: { enabled: false } // Disable native DNS
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Error Handling
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
import {
|
|
569
|
+
NoEnabledProvidersError,
|
|
570
|
+
InvalidHostnameError,
|
|
571
|
+
ProviderNotEnabledError,
|
|
572
|
+
InvalidProviderError
|
|
573
|
+
} from 'yq-dns';
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
const result = await resolve4('invalid..hostname');
|
|
577
|
+
} catch (error) {
|
|
578
|
+
if (error instanceof InvalidHostnameError) {
|
|
579
|
+
console.error('Invalid hostname:', error.message);
|
|
580
|
+
} else if (error instanceof NoEnabledProvidersError) {
|
|
581
|
+
console.error('No DNS providers are enabled');
|
|
582
|
+
} else if (error instanceof ProviderNotEnabledError) {
|
|
583
|
+
console.error('Requested provider is not enabled:', error.message);
|
|
584
|
+
} else {
|
|
585
|
+
console.error('DNS resolution failed:', error.message);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Concurrency Control
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
// High-throughput configuration
|
|
594
|
+
const highThroughputDns = new YqDns({
|
|
595
|
+
google: {
|
|
596
|
+
enabled: true,
|
|
597
|
+
limit: {
|
|
598
|
+
concurrency: 50, // High concurrency
|
|
599
|
+
interval: 1000,
|
|
600
|
+
intervalCap: 1000 // High rate limit
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
cloudflare: {
|
|
604
|
+
enabled: true,
|
|
605
|
+
limit: {
|
|
606
|
+
concurrency: 30,
|
|
607
|
+
interval: 1000,
|
|
608
|
+
intervalCap: 500
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// Conservative configuration
|
|
614
|
+
const conservativeDns = new YqDns({
|
|
615
|
+
native: {
|
|
616
|
+
enabled: true,
|
|
617
|
+
limit: {
|
|
618
|
+
concurrency: 2, // Low concurrency
|
|
619
|
+
interval: 2000, // Longer interval
|
|
620
|
+
intervalCap: 5 // Low rate limit
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## 📧 Email Validation System
|
|
627
|
+
|
|
628
|
+
### Domain Validation
|
|
629
|
+
|
|
630
|
+
Validate email domains with comprehensive analysis:
|
|
631
|
+
|
|
632
|
+
```typescript
|
|
633
|
+
const validator = YqDns.emailValidator;
|
|
634
|
+
|
|
635
|
+
// Basic domain validation
|
|
636
|
+
const result = await validator.validate('user@example.com');
|
|
637
|
+
if (result.success) {
|
|
638
|
+
console.log('Domain is valid');
|
|
639
|
+
console.log('Provider:', result.data.provider);
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Webmail Detection
|
|
644
|
+
|
|
645
|
+
Detect and identify webmail interfaces:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
const result = await validator.validate('user@gmail.com');
|
|
649
|
+
if (result.success && result.data.webmail) {
|
|
650
|
+
console.log('Webmail URL:', result.data.webmail);
|
|
651
|
+
// Output: https://mail.google.com
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Provider Recognition
|
|
656
|
+
|
|
657
|
+
Recognize 100+ email providers:
|
|
658
|
+
|
|
659
|
+
```typescript
|
|
660
|
+
const result = await validator.validate('user@outlook.com');
|
|
661
|
+
if (result.success) {
|
|
662
|
+
console.log('Provider:', result.data.provider);
|
|
663
|
+
// Output: 'Outlook'
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### Supported Email Providers
|
|
668
|
+
|
|
669
|
+
- **Major Providers**: Gmail, Outlook, Yahoo, iCloud, ProtonMail
|
|
670
|
+
- **Business**: Office 365, Google Workspace, Zoho, Fastmail
|
|
671
|
+
- **Regional**: 163.com, QQ.com, Mail.ru, Yandex, Naver
|
|
672
|
+
- **Hosting**: GoDaddy, Namecheap, Rackspace, Amazon SES
|
|
673
|
+
- **Security**: Mimecast, Proofpoint, Barracuda
|
|
674
|
+
- **And 80+ more providers...
|
|
675
|
+
|
|
676
|
+
## 🔷 TypeScript Support
|
|
677
|
+
|
|
678
|
+
### Full Type Safety
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
import { YqDns, MxRecord, SrvRecord, ProviderName } from 'yq-dns';
|
|
682
|
+
|
|
683
|
+
// Strongly typed configuration
|
|
684
|
+
const config: YqConfig = {
|
|
685
|
+
google: {
|
|
686
|
+
enabled: true,
|
|
687
|
+
limit: {
|
|
688
|
+
concurrency: 10,
|
|
689
|
+
interval: 1000,
|
|
690
|
+
intervalCap: 100
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
// Strongly typed results
|
|
696
|
+
const mxRecords: MxRecord[] = await resolveMx('example.com');
|
|
697
|
+
const srvRecords: SrvRecord[] = await resolveSrv('_sip._tcp.example.com');
|
|
698
|
+
|
|
699
|
+
// Type-safe provider names
|
|
700
|
+
const provider: ProviderName = 'google';
|
|
701
|
+
const addresses = await resolve4('example.com', undefined, provider);
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Generic Overloads
|
|
705
|
+
|
|
706
|
+
```typescript
|
|
707
|
+
// Method overloads for different return types
|
|
708
|
+
const addresses: string[] = await resolve4('example.com');
|
|
709
|
+
const withTtl: RecordWithTtl[] = await resolve4('example.com', { ttl: true });
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Custom Types
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
// Import specific types as needed
|
|
716
|
+
import type {
|
|
717
|
+
YqConfig,
|
|
718
|
+
ProviderConfig,
|
|
719
|
+
ProviderName,
|
|
720
|
+
RecordType,
|
|
721
|
+
AnyRecord,
|
|
722
|
+
RecordWithTtl,
|
|
723
|
+
ResolveOptions,
|
|
724
|
+
ResolveWithTtlOptions
|
|
725
|
+
} from 'yq-dns';
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
## ⚡ Performance
|
|
729
|
+
|
|
730
|
+
### Benchmarks
|
|
731
|
+
|
|
732
|
+
- **Concurrent Requests**: Up to 50 concurrent DNS requests per provider
|
|
733
|
+
- **Response Time**: Average 50-200ms depending on provider and location
|
|
734
|
+
- **Throughput**: 1000+ requests per second with optimal configuration
|
|
735
|
+
- **Memory Usage**: Minimal memory footprint with efficient queue management
|
|
736
|
+
|
|
737
|
+
### Optimization Tips
|
|
738
|
+
|
|
739
|
+
1. **Use Multiple Providers**: Enable multiple providers for better load distribution
|
|
740
|
+
2. **Tune Concurrency**: Adjust concurrency limits based on your use case
|
|
741
|
+
3. **Provider Selection**: Use faster providers for time-sensitive applications
|
|
742
|
+
4. **Caching**: Implement your own caching layer for frequently accessed domains
|
|
743
|
+
5. **Error Handling**: Implement proper retry logic for failed requests
|
|
744
|
+
|
|
745
|
+
### Performance Configuration
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
// High-performance configuration
|
|
749
|
+
const performanceDns = new YqDns({
|
|
750
|
+
google: {
|
|
751
|
+
enabled: true,
|
|
752
|
+
limit: { concurrency: 20, interval: 1000, intervalCap: 200 }
|
|
753
|
+
},
|
|
754
|
+
cloudflare: {
|
|
755
|
+
enabled: true,
|
|
756
|
+
limit: { concurrency: 20, interval: 1000, intervalCap: 200 }
|
|
757
|
+
},
|
|
758
|
+
quad9: {
|
|
759
|
+
enabled: true,
|
|
760
|
+
limit: { concurrency: 15, interval: 1000, intervalCap: 150 }
|
|
761
|
+
},
|
|
762
|
+
native: {
|
|
763
|
+
enabled: true,
|
|
764
|
+
limit: { concurrency: 10, interval: 1000, intervalCap: 100 }
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
## 📖 Examples
|
|
770
|
+
|
|
771
|
+
### Basic DNS Resolution
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
import { resolve4, resolveMx, resolveAny } from 'yq-dns';
|
|
775
|
+
|
|
776
|
+
// Resolve multiple record types
|
|
777
|
+
const [addresses, mxRecords, anyRecords] = await Promise.all([
|
|
778
|
+
resolve4('example.com'),
|
|
779
|
+
resolveMx('example.com'),
|
|
780
|
+
resolveAny('example.com')
|
|
781
|
+
]);
|
|
782
|
+
|
|
783
|
+
console.log('A Records:', addresses);
|
|
784
|
+
console.log('MX Records:', mxRecords);
|
|
785
|
+
console.log('All Records:', anyRecords);
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Email Domain Analysis
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
import { YqDns } from 'yq-dns';
|
|
792
|
+
|
|
793
|
+
const validator = YqDns.emailValidator;
|
|
794
|
+
|
|
795
|
+
async function analyzeEmailDomain(email: string) {
|
|
796
|
+
const result = await validator.validate(email);
|
|
797
|
+
|
|
798
|
+
if (result.success) {
|
|
799
|
+
return {
|
|
800
|
+
isValid: true,
|
|
801
|
+
provider: result.data.provider,
|
|
802
|
+
hasWebmail: !!result.data.webmail,
|
|
803
|
+
webmailUrl: result.data.webmail,
|
|
804
|
+
isRole: result.data.role
|
|
805
|
+
};
|
|
806
|
+
} else {
|
|
807
|
+
return {
|
|
808
|
+
isValid: false,
|
|
809
|
+
error: result.type,
|
|
810
|
+
message: result.message
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const analysis = await analyzeEmailDomain('user@gmail.com');
|
|
816
|
+
console.log(analysis);
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Bulk DNS Resolution
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
import { YqDns } from 'yq-dns';
|
|
823
|
+
|
|
824
|
+
const dns = new YqDns({
|
|
825
|
+
google: { enabled: true, limit: { concurrency: 10 } },
|
|
826
|
+
cloudflare: { enabled: true, limit: { concurrency: 10 } }
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
async function bulkResolve(hostnames: string[]) {
|
|
830
|
+
const results = await Promise.allSettled(
|
|
831
|
+
hostnames.map(hostname => dns.resolve4(hostname))
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
return results.map((result, index) => ({
|
|
835
|
+
hostname: hostnames[index],
|
|
836
|
+
success: result.status === 'fulfilled',
|
|
837
|
+
addresses: result.status === 'fulfilled' ? result.value : [],
|
|
838
|
+
error: result.status === 'rejected' ? result.reason.message : null
|
|
839
|
+
}));
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const domains = ['google.com', 'github.com', 'stackoverflow.com'];
|
|
843
|
+
const results = await bulkResolve(domains);
|
|
844
|
+
console.log(results);
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### Service Discovery
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
import { resolveSrv, resolveTxt } from 'yq-dns';
|
|
851
|
+
|
|
852
|
+
async function discoverServices(domain: string) {
|
|
853
|
+
const [sipServices, httpServices, txtRecords] = await Promise.all([
|
|
854
|
+
resolveSrv(`_sip._tcp.${domain}`).catch(() => []),
|
|
855
|
+
resolveSrv(`_http._tcp.${domain}`).catch(() => []),
|
|
856
|
+
resolveTxt(domain).catch(() => [])
|
|
857
|
+
]);
|
|
858
|
+
|
|
859
|
+
return {
|
|
860
|
+
sip: sipServices,
|
|
861
|
+
http: httpServices,
|
|
862
|
+
txt: txtRecords
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const services = await discoverServices('example.com');
|
|
867
|
+
console.log('Discovered services:', services);
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
### Custom Provider Configuration
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
import { YqDns } from 'yq-dns';
|
|
874
|
+
|
|
875
|
+
// Create DNS resolver with custom configuration
|
|
876
|
+
const customDns = new YqDns();
|
|
877
|
+
|
|
878
|
+
// Configure providers at runtime
|
|
879
|
+
customDns.setConfig('google', {
|
|
880
|
+
enabled: true,
|
|
881
|
+
limit: {
|
|
882
|
+
concurrency: 15,
|
|
883
|
+
interval: 1000,
|
|
884
|
+
intervalCap: 50
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
customDns.setConfig('cloudflare', {
|
|
889
|
+
enabled: true,
|
|
890
|
+
limit: {
|
|
891
|
+
concurrency: 10,
|
|
892
|
+
interval: 2000,
|
|
893
|
+
intervalCap: 30
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// Disable other providers
|
|
898
|
+
customDns.setConfigs({
|
|
899
|
+
quad9: { enabled: false },
|
|
900
|
+
native: { enabled: false }
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
const result = await customDns.resolve4('example.com');
|
|
904
|
+
console.log('Custom DNS result:', result);
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
## 🔧 Troubleshooting
|
|
908
|
+
|
|
909
|
+
### Common Issues
|
|
910
|
+
|
|
911
|
+
#### No Enabled Providers Error
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
// Problem: All providers are disabled
|
|
915
|
+
const dns = new YqDns({
|
|
916
|
+
google: { enabled: false },
|
|
917
|
+
cloudflare: { enabled: false },
|
|
918
|
+
quad9: { enabled: false },
|
|
919
|
+
native: { enabled: false }
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
// Solution: Enable at least one provider
|
|
923
|
+
const dns = new YqDns({
|
|
924
|
+
google: { enabled: true }
|
|
925
|
+
});
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
#### Rate Limiting Issues
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
// Problem: Too aggressive rate limiting
|
|
932
|
+
const dns = new YqDns({
|
|
933
|
+
google: {
|
|
934
|
+
enabled: true,
|
|
935
|
+
limit: {
|
|
936
|
+
concurrency: 1,
|
|
937
|
+
interval: 5000,
|
|
938
|
+
intervalCap: 1
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
// Solution: Increase limits
|
|
944
|
+
const dns = new YqDns({
|
|
945
|
+
google: {
|
|
946
|
+
enabled: true,
|
|
947
|
+
limit: {
|
|
948
|
+
concurrency: 10,
|
|
949
|
+
interval: 1000,
|
|
950
|
+
intervalCap: 50
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
#### Provider Not Available
|
|
957
|
+
|
|
958
|
+
```typescript
|
|
959
|
+
try {
|
|
960
|
+
const result = await resolve4('example.com', undefined, 'invalidprovider');
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (error instanceof InvalidProviderError) {
|
|
963
|
+
console.error('Invalid provider name');
|
|
964
|
+
// Use valid provider names: 'native', 'google', 'cloudflare', 'quad9'
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### Debug Mode
|
|
970
|
+
|
|
971
|
+
```typescript
|
|
972
|
+
// Enable debug logging (if implemented)
|
|
973
|
+
process.env.DEBUG = 'yq-dns:*';
|
|
974
|
+
|
|
975
|
+
// Or check queue status
|
|
976
|
+
const dns = new YqDns();
|
|
977
|
+
console.log('Queue sizes:', {
|
|
978
|
+
google: dns.getProvider('google')?.queueSize,
|
|
979
|
+
cloudflare: dns.getProvider('cloudflare')?.queueSize
|
|
980
|
+
});
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
### Performance Issues
|
|
984
|
+
|
|
985
|
+
1. **Check Provider Status**: Ensure providers are not at capacity
|
|
986
|
+
2. **Adjust Concurrency**: Increase concurrency limits if needed
|
|
987
|
+
3. **Enable Multiple Providers**: Use multiple providers for load distribution
|
|
988
|
+
4. **Monitor Network**: Check network connectivity to DoH endpoints
|
|
989
|
+
|
|
990
|
+
## 🤝 Contributing
|
|
991
|
+
|
|
992
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
993
|
+
|
|
994
|
+
### Development Setup
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
# Clone the repository
|
|
998
|
+
git clone https://github.com/yuniqsolutions/yq-dns.git
|
|
999
|
+
cd yq-dns
|
|
1000
|
+
|
|
1001
|
+
# Install dependencies
|
|
1002
|
+
npm install
|
|
1003
|
+
|
|
1004
|
+
# Build the project
|
|
1005
|
+
npm run build
|
|
1006
|
+
|
|
1007
|
+
# Run tests
|
|
1008
|
+
npm test
|
|
1009
|
+
|
|
1010
|
+
# Lint code
|
|
1011
|
+
npm run lint
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
### Reporting Issues
|
|
1015
|
+
|
|
1016
|
+
Please report issues on our [GitHub Issues](https://github.com/yuniqsolutions/yq-dns/issues) page.
|
|
1017
|
+
|
|
1018
|
+
## 📄 License
|
|
1019
|
+
|
|
1020
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1021
|
+
|
|
1022
|
+
---
|
|
1023
|
+
|
|
1024
|
+
**Made with ❤️ by [Yuniq Solutions Tech](https://yuniq.solutions)**
|
|
1025
|
+
|
|
1026
|
+
For more information, visit our [GitHub repository](https://github.com/yuniqsolutions/yq-dns) or check out our [documentation](https://github.com/yuniqsolutions/yq-dns#readme).
|