recker 1.0.79 → 1.0.80-next.70ea84d
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/browser/browser/mini.d.ts +2 -2
- package/dist/browser/browser/recker-mini.d.ts +8 -8
- package/dist/browser/browser/recker.d.ts +11 -8
- package/dist/browser/browser/recker.js +54 -6
- package/dist/browser/core/client.d.ts +15 -10
- package/dist/browser/core/client.js +54 -38
- package/dist/browser/index.iife.min.js +129 -130
- package/dist/browser/index.min.js +129 -130
- package/dist/browser/index.mini.iife.js +5697 -636
- package/dist/browser/index.mini.iife.min.js +47 -48
- package/dist/browser/index.mini.min.js +47 -48
- package/dist/browser/index.mini.umd.js +5697 -636
- package/dist/browser/index.mini.umd.min.js +47 -48
- package/dist/browser/index.umd.min.js +129 -130
- package/dist/browser/mini.d.ts +2 -2
- package/dist/browser/plugins/proxy-rotator.d.ts +2 -2
- package/dist/browser/plugins/proxy-rotator.js +6 -28
- package/dist/browser/recker-mini.d.ts +8 -8
- package/dist/browser/recker.d.ts +11 -8
- package/dist/browser/recker.js +54 -6
- package/dist/browser/scrape/document.js +2 -2
- package/dist/browser/scrape/element.js +7 -1
- package/dist/browser/scrape/parser/nodes/html.js +1 -1
- package/dist/browser/scrape/spider.d.ts +52 -0
- package/dist/browser/scrape/spider.js +620 -38
- package/dist/browser/scrape/types.d.ts +2 -0
- package/dist/browser/search/google.d.ts +26 -1
- package/dist/browser/search/google.js +427 -45
- package/dist/browser/seo/analyzer.d.ts +1 -0
- package/dist/browser/seo/analyzer.js +144 -1
- package/dist/browser/seo/index.d.ts +1 -1
- package/dist/browser/seo/keyword-campaign-analyzer.d.ts +2 -0
- package/dist/browser/seo/keyword-campaign-analyzer.js +538 -0
- package/dist/browser/seo/keyword-campaign-seed-advanced.d.ts +17 -0
- package/dist/browser/seo/keyword-campaign-seed-advanced.js +269 -0
- package/dist/browser/seo/keyword-campaign-seed-core.d.ts +29 -0
- package/dist/browser/seo/keyword-campaign-seed-core.js +525 -0
- package/dist/browser/seo/keyword-campaign-shared.d.ts +165 -0
- package/dist/browser/seo/keyword-campaign-shared.js +59 -0
- package/dist/browser/seo/keyword-campaign.d.ts +4 -107
- package/dist/browser/seo/keyword-campaign.js +2 -380
- package/dist/browser/seo/keywords.js +5 -22
- package/dist/browser/seo/types.d.ts +19 -0
- package/dist/browser/transport/curl.d.ts +5 -1
- package/dist/browser/transport/curl.js +207 -50
- package/dist/browser/transport/undici.d.ts +4 -3
- package/dist/browser/transport/undici.js +123 -49
- package/dist/browser/types/index.d.ts +9 -3
- package/dist/browser/utils/binary-manager.js +26 -3
- package/dist/browser/utils/block-detector.d.ts +8 -0
- package/dist/browser/utils/block-detector.js +542 -7
- package/dist/cli/commands/hls-runner.js +5 -4
- package/dist/cli/commands/live-runner.js +5 -4
- package/dist/cli/commands/loadtest-runner.js +3 -2
- package/dist/cli/commands/search.d.ts +2 -0
- package/dist/cli/commands/search.js +105 -0
- package/dist/cli/commands/seo-runner.js +9 -7
- package/dist/cli/commands/seo.js +140 -1
- package/dist/cli/commands/serve.js +75 -131
- package/dist/cli/commands/server-runner.js +59 -82
- package/dist/cli/commands/spider-runner.d.ts +37 -1
- package/dist/cli/commands/spider-runner.js +134 -10
- package/dist/cli/commands/spider.d.ts +18 -1
- package/dist/cli/commands/spider.js +457 -27
- package/dist/cli/events/handlers/cli.js +30 -1
- package/dist/cli/events/handlers/tui.js +26 -0
- package/dist/cli/events/types.d.ts +27 -0
- package/dist/cli/handler.d.ts +2 -12
- package/dist/cli/handler.js +20 -15
- package/dist/cli/handlers/protocols.js +39 -12
- package/dist/cli/handlers/search.d.ts +2 -0
- package/dist/cli/handlers/search.js +171 -0
- package/dist/cli/handlers/seo-analyze.d.ts +1 -0
- package/dist/cli/handlers/seo-analyze.js +666 -0
- package/dist/cli/handlers/seo-robots.d.ts +1 -0
- package/dist/cli/handlers/seo-robots.js +76 -0
- package/dist/cli/handlers/seo-serp.d.ts +54 -0
- package/dist/cli/handlers/seo-serp.js +243 -0
- package/dist/cli/handlers/seo-sitemap.d.ts +1 -0
- package/dist/cli/handlers/seo-sitemap.js +55 -0
- package/dist/cli/handlers/seo-spider.d.ts +1 -0
- package/dist/cli/handlers/seo-spider.js +334 -0
- package/dist/cli/handlers/seo.d.ts +8 -4
- package/dist/cli/handlers/seo.js +294 -442
- package/dist/cli/handlers/spider.js +94 -17
- package/dist/cli/handlers/streaming.js +5 -1
- package/dist/cli/index.js +11 -2
- package/dist/cli/presets.d.ts +1 -1
- package/dist/cli/presets.js +15 -4
- package/dist/cli/tui/app.js +6 -1
- package/dist/cli/tui/components/rich-response.d.ts +72 -0
- package/dist/cli/tui/components/rich-response.js +117 -0
- package/dist/cli/tui/executor-commands/background.js +30 -24
- package/dist/cli/tui/executor-commands/testing.js +3 -2
- package/dist/cli/tui/hooks/useDomains.d.ts +17 -0
- package/dist/cli/tui/hooks/useHelp.js +15 -2
- package/dist/cli/tui/job-manager.d.ts +4 -4
- package/dist/cli/tui/job-manager.js +5 -1
- package/dist/cli/tui/spider-tui.d.ts +63 -0
- package/dist/cli/tui/spider-tui.js +120 -2
- package/dist/cli/types.d.ts +12 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utils/option-helpers.d.ts +10 -0
- package/dist/cli/utils/option-helpers.js +63 -0
- package/dist/cli/utils/score-color.d.ts +11 -0
- package/dist/cli/utils/score-color.js +11 -0
- package/dist/cli/utils/serp-campaign.d.ts +53 -0
- package/dist/cli/utils/serp-campaign.js +53 -0
- package/dist/cli/utils/serp-config.d.ts +26 -0
- package/dist/cli/utils/serp-config.js +125 -0
- package/dist/core/client.d.ts +15 -10
- package/dist/core/client.js +54 -38
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/cli.js +35 -34
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +20 -2
- package/dist/mcp/contract.d.ts +1 -1
- package/dist/mcp/profiles.js +5 -1
- package/dist/mcp/prompts/index.js +8 -4
- package/dist/mcp/resources/index.js +46 -23
- package/dist/mcp/server.js +9 -6
- package/dist/mcp/tools/protocols.js +9 -2
- package/dist/mcp/tools/registry.js +13 -3
- package/dist/mcp/tools/seo.js +427 -2
- package/dist/mcp/types.d.ts +5 -1
- package/dist/plugins/proxy-rotator.d.ts +2 -2
- package/dist/plugins/proxy-rotator.js +6 -28
- package/dist/raffel/client.d.ts +38 -0
- package/dist/raffel/client.js +282 -0
- package/dist/raffel/index.d.ts +2 -0
- package/dist/raffel/index.js +2 -0
- package/dist/raffel/types.d.ts +40 -0
- package/dist/raffel/types.js +14 -0
- package/dist/recker.d.ts +13 -7
- package/dist/recker.js +58 -6
- package/dist/scrape/document.js +2 -2
- package/dist/scrape/element.js +7 -1
- package/dist/scrape/parser/nodes/html.js +1 -1
- package/dist/scrape/spider.d.ts +52 -0
- package/dist/scrape/spider.js +620 -38
- package/dist/scrape/types.d.ts +2 -0
- package/dist/search/google.d.ts +26 -1
- package/dist/search/google.js +427 -45
- package/dist/search/index.d.ts +1 -1
- package/dist/seo/analyzer.d.ts +1 -0
- package/dist/seo/analyzer.js +144 -1
- package/dist/seo/index.d.ts +1 -1
- package/dist/seo/keyword-campaign-analyzer.d.ts +2 -0
- package/dist/seo/keyword-campaign-analyzer.js +538 -0
- package/dist/seo/keyword-campaign-seed-advanced.d.ts +17 -0
- package/dist/seo/keyword-campaign-seed-advanced.js +269 -0
- package/dist/seo/keyword-campaign-seed-core.d.ts +29 -0
- package/dist/seo/keyword-campaign-seed-core.js +525 -0
- package/dist/seo/keyword-campaign-shared.d.ts +165 -0
- package/dist/seo/keyword-campaign-shared.js +59 -0
- package/dist/seo/keyword-campaign.d.ts +4 -107
- package/dist/seo/keyword-campaign.js +2 -380
- package/dist/seo/keywords.js +5 -22
- package/dist/seo/types.d.ts +19 -0
- package/dist/template/index.d.ts +1 -1
- package/dist/template/types.d.ts +0 -2
- package/dist/testing/index.d.ts +1 -22
- package/dist/testing/index.js +1 -11
- package/dist/transport/curl.d.ts +5 -1
- package/dist/transport/curl.js +207 -50
- package/dist/transport/undici.d.ts +4 -3
- package/dist/transport/undici.js +123 -49
- package/dist/types/index.d.ts +9 -3
- package/dist/utils/binary-manager.js +26 -3
- package/dist/utils/block-detector.d.ts +8 -0
- package/dist/utils/block-detector.js +542 -7
- package/dist/version.js +1 -1
- package/package.json +12 -1
- package/dist/testing/mock-dns-server.d.ts +0 -69
- package/dist/testing/mock-dns-server.js +0 -269
- package/dist/testing/mock-ftp-server.d.ts +0 -89
- package/dist/testing/mock-ftp-server.js +0 -562
- package/dist/testing/mock-hls-server.d.ts +0 -80
- package/dist/testing/mock-hls-server.js +0 -381
- package/dist/testing/mock-http-server.d.ts +0 -124
- package/dist/testing/mock-http-server.js +0 -343
- package/dist/testing/mock-proxy-server.d.ts +0 -108
- package/dist/testing/mock-proxy-server.js +0 -615
- package/dist/testing/mock-sse-server.d.ts +0 -76
- package/dist/testing/mock-sse-server.js +0 -291
- package/dist/testing/mock-telnet-server.d.ts +0 -59
- package/dist/testing/mock-telnet-server.js +0 -274
- package/dist/testing/mock-udp-server.d.ts +0 -43
- package/dist/testing/mock-udp-server.js +0 -188
- package/dist/testing/mock-websocket-server.d.ts +0 -76
- package/dist/testing/mock-websocket-server.js +0 -334
- package/dist/testing/mock-whois-server.d.ts +0 -56
- package/dist/testing/mock-whois-server.js +0 -234
- package/dist/testing/proxy-certs.d.ts +0 -19
- package/dist/testing/proxy-certs.js +0 -208
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.80-next.70ea84d",
|
|
4
4
|
"description": "Multi-Protocol SDK for the AI Era - HTTP, WebSocket, DNS, FTP, SFTP, Telnet, HLS unified with AI providers and MCP tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -211,6 +211,10 @@
|
|
|
211
211
|
"types": "./dist/presets/*.d.ts",
|
|
212
212
|
"import": "./dist/presets/*.js"
|
|
213
213
|
},
|
|
214
|
+
"./raffel": {
|
|
215
|
+
"types": "./dist/raffel/index.d.ts",
|
|
216
|
+
"import": "./dist/raffel/index.js"
|
|
217
|
+
},
|
|
214
218
|
"./package.json": "./package.json"
|
|
215
219
|
},
|
|
216
220
|
"browser": "./dist/browser/browser/index.js",
|
|
@@ -239,6 +243,7 @@
|
|
|
239
243
|
"peerDependencies": {
|
|
240
244
|
"cardinal": "^2.1.0",
|
|
241
245
|
"ioredis": "^5.0.0",
|
|
246
|
+
"socks": "^2.8.0",
|
|
242
247
|
"ssh2-sftp-client": "^11.0.0",
|
|
243
248
|
"ws": "^8.0.0"
|
|
244
249
|
},
|
|
@@ -249,6 +254,9 @@
|
|
|
249
254
|
"ioredis": {
|
|
250
255
|
"optional": true
|
|
251
256
|
},
|
|
257
|
+
"socks": {
|
|
258
|
+
"optional": true
|
|
259
|
+
},
|
|
252
260
|
"ssh2-sftp-client": {
|
|
253
261
|
"optional": true
|
|
254
262
|
},
|
|
@@ -257,6 +265,7 @@
|
|
|
257
265
|
}
|
|
258
266
|
},
|
|
259
267
|
"devDependencies": {
|
|
268
|
+
"raffel": "^1.0.3",
|
|
260
269
|
"@hapi/wreck": "^18.1.0",
|
|
261
270
|
"@types/he": "^1.2.3",
|
|
262
271
|
"@types/needle": "^3.3.0",
|
|
@@ -282,6 +291,7 @@
|
|
|
282
291
|
"picocolors": "^1.1.1",
|
|
283
292
|
"popsicle": "^12.1.2",
|
|
284
293
|
"serve": "^14.2.5",
|
|
294
|
+
"socks": "^2.8.3",
|
|
285
295
|
"ssh2-sftp-client": "^12.0.1",
|
|
286
296
|
"superagent": "^10.3.0",
|
|
287
297
|
"tsx": "^4.21.0",
|
|
@@ -300,6 +310,7 @@
|
|
|
300
310
|
"build:binary": "npx pkg ./dist/bin/rek.cjs --output dist/bin/recker --targets node18-linux-x64,node18-macos-x64,node18-win-x64",
|
|
301
311
|
"test": "NODE_OPTIONS='--max-old-space-size=512' vitest run",
|
|
302
312
|
"test:coverage": "NODE_OPTIONS='--max-old-space-size=512' vitest run --coverage",
|
|
313
|
+
"test:curl-impersonate:coverage": "NODE_OPTIONS='--max-old-space-size=512' vitest run --config vitest.config.curl-impersonate.ts --coverage",
|
|
303
314
|
"test:light": "NODE_OPTIONS='--max-old-space-size=256' vitest run --maxWorkers=1 --fileParallelism=false",
|
|
304
315
|
"test:browser": "vitest run --config vitest.config.browser.ts",
|
|
305
316
|
"test:browser:coverage": "vitest run --config vitest.config.browser.ts --coverage",
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
export interface MockDnsServerOptions {
|
|
3
|
-
port?: number;
|
|
4
|
-
host?: string;
|
|
5
|
-
ttl?: number;
|
|
6
|
-
delay?: number;
|
|
7
|
-
}
|
|
8
|
-
export type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT' | 'NS' | 'SOA' | 'PTR' | 'SRV';
|
|
9
|
-
export interface DnsRecord {
|
|
10
|
-
type: DnsRecordType;
|
|
11
|
-
value: string | DnsMxRecord | DnsSoaRecord | DnsSrvRecord;
|
|
12
|
-
ttl?: number;
|
|
13
|
-
}
|
|
14
|
-
export interface DnsMxRecord {
|
|
15
|
-
priority: number;
|
|
16
|
-
exchange: string;
|
|
17
|
-
}
|
|
18
|
-
export interface DnsSoaRecord {
|
|
19
|
-
mname: string;
|
|
20
|
-
rname: string;
|
|
21
|
-
serial: number;
|
|
22
|
-
refresh: number;
|
|
23
|
-
retry: number;
|
|
24
|
-
expire: number;
|
|
25
|
-
minimum: number;
|
|
26
|
-
}
|
|
27
|
-
export interface DnsSrvRecord {
|
|
28
|
-
priority: number;
|
|
29
|
-
weight: number;
|
|
30
|
-
port: number;
|
|
31
|
-
target: string;
|
|
32
|
-
}
|
|
33
|
-
export interface MockDnsStats {
|
|
34
|
-
queriesReceived: number;
|
|
35
|
-
responseSent: number;
|
|
36
|
-
queryLog: Array<{
|
|
37
|
-
domain: string;
|
|
38
|
-
type: string;
|
|
39
|
-
timestamp: number;
|
|
40
|
-
}>;
|
|
41
|
-
}
|
|
42
|
-
export declare class MockDnsServer extends EventEmitter {
|
|
43
|
-
private options;
|
|
44
|
-
private socket;
|
|
45
|
-
private records;
|
|
46
|
-
private started;
|
|
47
|
-
private stats;
|
|
48
|
-
constructor(options?: MockDnsServerOptions);
|
|
49
|
-
get isRunning(): boolean;
|
|
50
|
-
get port(): number;
|
|
51
|
-
get host(): string;
|
|
52
|
-
get statistics(): MockDnsStats;
|
|
53
|
-
addRecord(domain: string, type: DnsRecordType, value: string | DnsMxRecord | DnsSoaRecord | DnsSrvRecord, ttl?: number): void;
|
|
54
|
-
removeRecords(domain: string): void;
|
|
55
|
-
getRecords(domain: string): DnsRecord[];
|
|
56
|
-
clearRecords(): void;
|
|
57
|
-
private addDefaultRecords;
|
|
58
|
-
start(): Promise<void>;
|
|
59
|
-
stop(): Promise<void>;
|
|
60
|
-
reset(): void;
|
|
61
|
-
private handleQuery;
|
|
62
|
-
private parseDnsQuery;
|
|
63
|
-
private buildResponse;
|
|
64
|
-
private getDomainLength;
|
|
65
|
-
private buildAnswerRecord;
|
|
66
|
-
private encodeRdata;
|
|
67
|
-
private encodeDomainName;
|
|
68
|
-
static create(options?: MockDnsServerOptions): Promise<MockDnsServer>;
|
|
69
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import * as dgram from 'node:dgram';
|
|
3
|
-
const RECORD_TYPES = {
|
|
4
|
-
A: 1,
|
|
5
|
-
NS: 2,
|
|
6
|
-
CNAME: 5,
|
|
7
|
-
SOA: 6,
|
|
8
|
-
PTR: 12,
|
|
9
|
-
MX: 15,
|
|
10
|
-
TXT: 16,
|
|
11
|
-
AAAA: 28,
|
|
12
|
-
SRV: 33,
|
|
13
|
-
};
|
|
14
|
-
const RECORD_TYPE_NAMES = Object.fromEntries(Object.entries(RECORD_TYPES).map(([k, v]) => [v, k]));
|
|
15
|
-
export class MockDnsServer extends EventEmitter {
|
|
16
|
-
options;
|
|
17
|
-
socket = null;
|
|
18
|
-
records = new Map();
|
|
19
|
-
started = false;
|
|
20
|
-
stats = {
|
|
21
|
-
queriesReceived: 0,
|
|
22
|
-
responseSent: 0,
|
|
23
|
-
queryLog: [],
|
|
24
|
-
};
|
|
25
|
-
constructor(options = {}) {
|
|
26
|
-
super();
|
|
27
|
-
this.options = {
|
|
28
|
-
port: 5353,
|
|
29
|
-
host: '127.0.0.1',
|
|
30
|
-
ttl: 300,
|
|
31
|
-
delay: 0,
|
|
32
|
-
...options,
|
|
33
|
-
};
|
|
34
|
-
this.addDefaultRecords();
|
|
35
|
-
}
|
|
36
|
-
get isRunning() {
|
|
37
|
-
return this.started;
|
|
38
|
-
}
|
|
39
|
-
get port() {
|
|
40
|
-
return this.options.port;
|
|
41
|
-
}
|
|
42
|
-
get host() {
|
|
43
|
-
return this.options.host;
|
|
44
|
-
}
|
|
45
|
-
get statistics() {
|
|
46
|
-
return { ...this.stats };
|
|
47
|
-
}
|
|
48
|
-
addRecord(domain, type, value, ttl) {
|
|
49
|
-
const key = domain.toLowerCase();
|
|
50
|
-
const records = this.records.get(key) || [];
|
|
51
|
-
records.push({ type, value, ttl: ttl ?? this.options.ttl });
|
|
52
|
-
this.records.set(key, records);
|
|
53
|
-
}
|
|
54
|
-
removeRecords(domain) {
|
|
55
|
-
this.records.delete(domain.toLowerCase());
|
|
56
|
-
}
|
|
57
|
-
getRecords(domain) {
|
|
58
|
-
return this.records.get(domain.toLowerCase()) || [];
|
|
59
|
-
}
|
|
60
|
-
clearRecords() {
|
|
61
|
-
this.records.clear();
|
|
62
|
-
this.addDefaultRecords();
|
|
63
|
-
}
|
|
64
|
-
addDefaultRecords() {
|
|
65
|
-
this.addRecord('localhost', 'A', '127.0.0.1');
|
|
66
|
-
this.addRecord('localhost', 'AAAA', '::1');
|
|
67
|
-
this.addRecord('example.com', 'A', '93.184.216.34');
|
|
68
|
-
this.addRecord('example.com', 'AAAA', '2606:2800:220:1:248:1893:25c8:1946');
|
|
69
|
-
this.addRecord('example.com', 'NS', 'ns1.example.com');
|
|
70
|
-
this.addRecord('example.com', 'NS', 'ns2.example.com');
|
|
71
|
-
this.addRecord('example.com', 'MX', { priority: 10, exchange: 'mail.example.com' });
|
|
72
|
-
this.addRecord('example.com', 'TXT', 'v=spf1 include:_spf.example.com ~all');
|
|
73
|
-
this.addRecord('test.local', 'A', '192.168.1.100');
|
|
74
|
-
this.addRecord('api.test.local', 'A', '192.168.1.101');
|
|
75
|
-
this.addRecord('api.test.local', 'CNAME', 'test.local');
|
|
76
|
-
}
|
|
77
|
-
async start() {
|
|
78
|
-
if (this.started) {
|
|
79
|
-
throw new Error('Server already started');
|
|
80
|
-
}
|
|
81
|
-
return new Promise((resolve, reject) => {
|
|
82
|
-
this.socket = dgram.createSocket('udp4');
|
|
83
|
-
this.socket.on('error', (err) => {
|
|
84
|
-
this.emit('error', err);
|
|
85
|
-
if (!this.started) {
|
|
86
|
-
reject(err);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
this.socket.on('message', (msg, rinfo) => {
|
|
90
|
-
this.handleQuery(msg, rinfo);
|
|
91
|
-
});
|
|
92
|
-
this.socket.bind(this.options.port, this.options.host, () => {
|
|
93
|
-
this.started = true;
|
|
94
|
-
this.emit('start');
|
|
95
|
-
resolve();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
async stop() {
|
|
100
|
-
if (!this.started || !this.socket)
|
|
101
|
-
return;
|
|
102
|
-
return new Promise((resolve) => {
|
|
103
|
-
this.socket.close(() => {
|
|
104
|
-
this.socket = null;
|
|
105
|
-
this.started = false;
|
|
106
|
-
this.emit('stop');
|
|
107
|
-
resolve();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
reset() {
|
|
112
|
-
this.stats = {
|
|
113
|
-
queriesReceived: 0,
|
|
114
|
-
responseSent: 0,
|
|
115
|
-
queryLog: [],
|
|
116
|
-
};
|
|
117
|
-
this.clearRecords();
|
|
118
|
-
this.emit('reset');
|
|
119
|
-
}
|
|
120
|
-
async handleQuery(msg, rinfo) {
|
|
121
|
-
this.stats.queriesReceived++;
|
|
122
|
-
if (this.options.delay > 0) {
|
|
123
|
-
await new Promise((resolve) => setTimeout(resolve, this.options.delay));
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
const query = this.parseDnsQuery(msg);
|
|
127
|
-
this.stats.queryLog.push({
|
|
128
|
-
domain: query.domain,
|
|
129
|
-
type: query.type,
|
|
130
|
-
timestamp: Date.now(),
|
|
131
|
-
});
|
|
132
|
-
this.emit('query', query, rinfo);
|
|
133
|
-
const response = this.buildResponse(msg, query);
|
|
134
|
-
this.socket?.send(response, rinfo.port, rinfo.address, (err) => {
|
|
135
|
-
if (err) {
|
|
136
|
-
this.emit('error', err);
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
this.stats.responseSent++;
|
|
140
|
-
this.emit('response', query);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
catch (err) {
|
|
145
|
-
this.emit('error', err);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
parseDnsQuery(msg) {
|
|
149
|
-
const id = msg.readUInt16BE(0);
|
|
150
|
-
let offset = 12;
|
|
151
|
-
const labels = [];
|
|
152
|
-
while (msg[offset] !== 0) {
|
|
153
|
-
const len = msg[offset];
|
|
154
|
-
offset++;
|
|
155
|
-
labels.push(msg.slice(offset, offset + len).toString('ascii'));
|
|
156
|
-
offset += len;
|
|
157
|
-
}
|
|
158
|
-
offset++;
|
|
159
|
-
const domain = labels.join('.');
|
|
160
|
-
const typeCode = msg.readUInt16BE(offset);
|
|
161
|
-
const type = RECORD_TYPE_NAMES[typeCode] || `TYPE${typeCode}`;
|
|
162
|
-
return { id, domain, type, typeCode };
|
|
163
|
-
}
|
|
164
|
-
buildResponse(query, parsed) {
|
|
165
|
-
const records = this.records.get(parsed.domain.toLowerCase()) || [];
|
|
166
|
-
const matchingRecords = records.filter((r) => r.type === parsed.type || parsed.type === 'ANY');
|
|
167
|
-
const header = Buffer.alloc(12);
|
|
168
|
-
header.writeUInt16BE(parsed.id, 0);
|
|
169
|
-
header.writeUInt16BE(0x8180, 2);
|
|
170
|
-
header.writeUInt16BE(1, 4);
|
|
171
|
-
header.writeUInt16BE(matchingRecords.length, 6);
|
|
172
|
-
header.writeUInt16BE(0, 8);
|
|
173
|
-
header.writeUInt16BE(0, 10);
|
|
174
|
-
const questionEnd = 12 + this.getDomainLength(query, 12) + 4;
|
|
175
|
-
const question = query.slice(12, questionEnd);
|
|
176
|
-
const answers = [];
|
|
177
|
-
for (const record of matchingRecords) {
|
|
178
|
-
answers.push(this.buildAnswerRecord(parsed.domain, record));
|
|
179
|
-
}
|
|
180
|
-
return Buffer.concat([header, question, ...answers]);
|
|
181
|
-
}
|
|
182
|
-
getDomainLength(buf, offset) {
|
|
183
|
-
let len = 0;
|
|
184
|
-
while (buf[offset + len] !== 0) {
|
|
185
|
-
len += buf[offset + len] + 1;
|
|
186
|
-
}
|
|
187
|
-
return len + 1;
|
|
188
|
-
}
|
|
189
|
-
buildAnswerRecord(domain, record) {
|
|
190
|
-
const parts = [];
|
|
191
|
-
parts.push(Buffer.from([0xc0, 0x0c]));
|
|
192
|
-
const typeCode = RECORD_TYPES[record.type] || 1;
|
|
193
|
-
const typeBuf = Buffer.alloc(2);
|
|
194
|
-
typeBuf.writeUInt16BE(typeCode, 0);
|
|
195
|
-
parts.push(typeBuf);
|
|
196
|
-
const classBuf = Buffer.alloc(2);
|
|
197
|
-
classBuf.writeUInt16BE(1, 0);
|
|
198
|
-
parts.push(classBuf);
|
|
199
|
-
const ttlBuf = Buffer.alloc(4);
|
|
200
|
-
ttlBuf.writeUInt32BE(record.ttl ?? this.options.ttl, 0);
|
|
201
|
-
parts.push(ttlBuf);
|
|
202
|
-
const rdata = this.encodeRdata(record);
|
|
203
|
-
const rdLengthBuf = Buffer.alloc(2);
|
|
204
|
-
rdLengthBuf.writeUInt16BE(rdata.length, 0);
|
|
205
|
-
parts.push(rdLengthBuf);
|
|
206
|
-
parts.push(rdata);
|
|
207
|
-
return Buffer.concat(parts);
|
|
208
|
-
}
|
|
209
|
-
encodeRdata(record) {
|
|
210
|
-
switch (record.type) {
|
|
211
|
-
case 'A': {
|
|
212
|
-
const parts = record.value.split('.').map(Number);
|
|
213
|
-
return Buffer.from(parts);
|
|
214
|
-
}
|
|
215
|
-
case 'AAAA': {
|
|
216
|
-
const ip = record.value;
|
|
217
|
-
const buf = Buffer.alloc(16);
|
|
218
|
-
const parts = ip.split(':');
|
|
219
|
-
for (let i = 0; i < 8; i++) {
|
|
220
|
-
const val = parseInt(parts[i] || '0', 16);
|
|
221
|
-
buf.writeUInt16BE(val, i * 2);
|
|
222
|
-
}
|
|
223
|
-
return buf;
|
|
224
|
-
}
|
|
225
|
-
case 'CNAME':
|
|
226
|
-
case 'NS':
|
|
227
|
-
case 'PTR': {
|
|
228
|
-
return this.encodeDomainName(record.value);
|
|
229
|
-
}
|
|
230
|
-
case 'MX': {
|
|
231
|
-
const mx = record.value;
|
|
232
|
-
const priority = Buffer.alloc(2);
|
|
233
|
-
priority.writeUInt16BE(mx.priority, 0);
|
|
234
|
-
return Buffer.concat([priority, this.encodeDomainName(mx.exchange)]);
|
|
235
|
-
}
|
|
236
|
-
case 'TXT': {
|
|
237
|
-
const txt = record.value;
|
|
238
|
-
const txtBuf = Buffer.from(txt, 'utf8');
|
|
239
|
-
const len = Buffer.from([txtBuf.length]);
|
|
240
|
-
return Buffer.concat([len, txtBuf]);
|
|
241
|
-
}
|
|
242
|
-
case 'SRV': {
|
|
243
|
-
const srv = record.value;
|
|
244
|
-
const buf = Buffer.alloc(6);
|
|
245
|
-
buf.writeUInt16BE(srv.priority, 0);
|
|
246
|
-
buf.writeUInt16BE(srv.weight, 2);
|
|
247
|
-
buf.writeUInt16BE(srv.port, 4);
|
|
248
|
-
return Buffer.concat([buf, this.encodeDomainName(srv.target)]);
|
|
249
|
-
}
|
|
250
|
-
default:
|
|
251
|
-
return Buffer.from(record.value, 'utf8');
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
encodeDomainName(domain) {
|
|
255
|
-
const parts = domain.split('.');
|
|
256
|
-
const buffers = [];
|
|
257
|
-
for (const part of parts) {
|
|
258
|
-
buffers.push(Buffer.from([part.length]));
|
|
259
|
-
buffers.push(Buffer.from(part, 'ascii'));
|
|
260
|
-
}
|
|
261
|
-
buffers.push(Buffer.from([0]));
|
|
262
|
-
return Buffer.concat(buffers);
|
|
263
|
-
}
|
|
264
|
-
static async create(options = {}) {
|
|
265
|
-
const server = new MockDnsServer(options);
|
|
266
|
-
await server.start();
|
|
267
|
-
return server;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import * as net from 'node:net';
|
|
3
|
-
export interface MockFtpServerOptions {
|
|
4
|
-
port?: number;
|
|
5
|
-
host?: string;
|
|
6
|
-
anonymous?: boolean;
|
|
7
|
-
username?: string;
|
|
8
|
-
password?: string;
|
|
9
|
-
welcomeMessage?: string;
|
|
10
|
-
delay?: number;
|
|
11
|
-
}
|
|
12
|
-
export interface VirtualFile {
|
|
13
|
-
content: string | Buffer;
|
|
14
|
-
size: number;
|
|
15
|
-
modified: Date;
|
|
16
|
-
isDirectory: boolean;
|
|
17
|
-
}
|
|
18
|
-
export interface FtpSession {
|
|
19
|
-
id: string;
|
|
20
|
-
socket: net.Socket;
|
|
21
|
-
authenticated: boolean;
|
|
22
|
-
username: string | null;
|
|
23
|
-
currentDir: string;
|
|
24
|
-
transferMode: 'ASCII' | 'BINARY';
|
|
25
|
-
passiveSocket: net.Server | null;
|
|
26
|
-
connectedAt: Date;
|
|
27
|
-
}
|
|
28
|
-
export interface MockFtpStats {
|
|
29
|
-
connectionsTotal: number;
|
|
30
|
-
commandsReceived: number;
|
|
31
|
-
filesDownloaded: number;
|
|
32
|
-
filesUploaded: number;
|
|
33
|
-
bytesTransferred: number;
|
|
34
|
-
commandLog: Array<{
|
|
35
|
-
command: string;
|
|
36
|
-
sessionId: string;
|
|
37
|
-
timestamp: number;
|
|
38
|
-
}>;
|
|
39
|
-
}
|
|
40
|
-
export declare class MockFtpServer extends EventEmitter {
|
|
41
|
-
private options;
|
|
42
|
-
private server;
|
|
43
|
-
private sessions;
|
|
44
|
-
private files;
|
|
45
|
-
private started;
|
|
46
|
-
private sessionCounter;
|
|
47
|
-
private dataPortCounter;
|
|
48
|
-
private stats;
|
|
49
|
-
constructor(options?: MockFtpServerOptions);
|
|
50
|
-
get isRunning(): boolean;
|
|
51
|
-
get port(): number;
|
|
52
|
-
get host(): string;
|
|
53
|
-
get url(): string;
|
|
54
|
-
get statistics(): MockFtpStats;
|
|
55
|
-
addFile(path: string, content: string | Buffer): void;
|
|
56
|
-
addDirectory(path: string): void;
|
|
57
|
-
removeFile(path: string): boolean;
|
|
58
|
-
getFile(path: string): VirtualFile | undefined;
|
|
59
|
-
listDirectory(path: string): string[];
|
|
60
|
-
clearFiles(): void;
|
|
61
|
-
private normalizePath;
|
|
62
|
-
private addDefaultFiles;
|
|
63
|
-
start(): Promise<void>;
|
|
64
|
-
stop(): Promise<void>;
|
|
65
|
-
reset(): void;
|
|
66
|
-
private handleConnection;
|
|
67
|
-
private handleCommand;
|
|
68
|
-
private handleUser;
|
|
69
|
-
private handlePass;
|
|
70
|
-
private handleCwd;
|
|
71
|
-
private handleCdup;
|
|
72
|
-
private handleType;
|
|
73
|
-
private handlePasv;
|
|
74
|
-
private handleList;
|
|
75
|
-
private handleNlst;
|
|
76
|
-
private handleRetr;
|
|
77
|
-
private handleStor;
|
|
78
|
-
private handleSize;
|
|
79
|
-
private handleMdtm;
|
|
80
|
-
private handleMkd;
|
|
81
|
-
private handleRmd;
|
|
82
|
-
private handleDele;
|
|
83
|
-
private requireAuth;
|
|
84
|
-
private resolvePath;
|
|
85
|
-
private sendResponse;
|
|
86
|
-
private sendMultilineResponse;
|
|
87
|
-
private waitForDataConnection;
|
|
88
|
-
static create(options?: MockFtpServerOptions): Promise<MockFtpServer>;
|
|
89
|
-
}
|