recker 1.0.19 → 1.0.20-next.595cc59
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/cli/index.d.ts +0 -1
- package/dist/cli/index.js +495 -2
- package/dist/cli/tui/shell.js +1 -1
- package/dist/core/client.d.ts +2 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +6 -7
- package/dist/plugins/cache.js +1 -1
- package/dist/plugins/hls.d.ts +90 -17
- package/dist/plugins/hls.d.ts.map +1 -1
- package/dist/plugins/hls.js +343 -173
- package/dist/plugins/retry.js +2 -2
- package/dist/testing/index.d.ts +16 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +8 -0
- package/dist/testing/mock-dns-server.d.ts +70 -0
- package/dist/testing/mock-dns-server.d.ts.map +1 -0
- package/dist/testing/mock-dns-server.js +269 -0
- package/dist/testing/mock-ftp-server.d.ts +90 -0
- package/dist/testing/mock-ftp-server.d.ts.map +1 -0
- package/dist/testing/mock-ftp-server.js +562 -0
- package/dist/testing/mock-hls-server.d.ts +81 -0
- package/dist/testing/mock-hls-server.d.ts.map +1 -0
- package/dist/testing/mock-hls-server.js +381 -0
- package/dist/testing/mock-http-server.d.ts +100 -0
- package/dist/testing/mock-http-server.d.ts.map +1 -0
- package/dist/testing/mock-http-server.js +298 -0
- package/dist/testing/mock-sse-server.d.ts +77 -0
- package/dist/testing/mock-sse-server.d.ts.map +1 -0
- package/dist/testing/mock-sse-server.js +291 -0
- package/dist/testing/mock-telnet-server.d.ts +60 -0
- package/dist/testing/mock-telnet-server.d.ts.map +1 -0
- package/dist/testing/mock-telnet-server.js +273 -0
- package/dist/testing/mock-websocket-server.d.ts +78 -0
- package/dist/testing/mock-websocket-server.d.ts.map +1 -0
- package/dist/testing/mock-websocket-server.js +316 -0
- package/dist/testing/mock-whois-server.d.ts +57 -0
- package/dist/testing/mock-whois-server.d.ts.map +1 -0
- package/dist/testing/mock-whois-server.js +234 -0
- package/dist/transport/undici.d.ts +1 -1
- package/dist/transport/undici.d.ts.map +1 -1
- package/dist/transport/undici.js +11 -5
- package/dist/utils/dns-toolkit.js +1 -1
- package/dist/utils/dns.js +2 -2
- package/dist/utils/optional-require.js +1 -1
- package/dist/webrtc/index.js +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
}
|
|
70
|
+
//# sourceMappingURL=mock-dns-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-dns-server.d.ts","sourceRoot":"","sources":["../../src/testing/mock-dns-server.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,MAAM,WAAW,oBAAoB;IAKnC,IAAI,CAAC,EAAE,MAAM,CAAC;IAMd,IAAI,CAAC,EAAE,MAAM,CAAC;IAMd,GAAG,CAAC,EAAE,MAAM,CAAC;IAMb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,aAAa,GAAG,GAAG,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEjG,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtE;AAuBD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAIX;gBAEU,OAAO,GAAE,oBAAyB;IAmB9C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,UAAU,IAAI,YAAY,CAE7B;IASD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAU7H,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOnC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAOvC,YAAY,IAAI,IAAI;IAKpB,OAAO,CAAC,iBAAiB;IAuBnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,KAAK,IAAI,IAAI;YAcC,WAAW;IAkCzB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,WAAW;IA8CnB,OAAO,CAAC,gBAAgB;WAeX,MAAM,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,aAAa,CAAC;CAKhF"}
|
|
@@ -0,0 +1,269 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
}
|
|
90
|
+
//# sourceMappingURL=mock-ftp-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-ftp-server.d.ts","sourceRoot":"","sources":["../../src/testing/mock-ftp-server.ts"],"names":[],"mappings":"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"}
|