@omindu/yaksha 1.0.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/LICENSE +21 -0
- package/bin/yaksha.js +329 -0
- package/package.json +28 -0
- package/src/bypass/firewall-evasion.js +354 -0
- package/src/client/index.js +544 -0
- package/src/core/connection.js +393 -0
- package/src/core/encryption.js +299 -0
- package/src/core/protocol.js +268 -0
- package/src/features/dns-override.js +403 -0
- package/src/features/multi-path.js +394 -0
- package/src/features/sni-spoof.js +355 -0
- package/src/features/tls-camouflage.js +369 -0
- package/src/features/traffic-obfuscation.js +338 -0
- package/src/index.js +106 -0
- package/src/security/auth.js +441 -0
- package/src/security/levels.js +316 -0
- package/src/server/index.js +551 -0
- package/src/utils/buffer-pool.js +150 -0
- package/src/utils/config.js +205 -0
- package/src/utils/logger.js +105 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yaksha Multi-Path Routing
|
|
5
|
+
* Establish multiple connections and distribute traffic
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
const Connection = require('../core/connection');
|
|
10
|
+
const Logger = require('../utils/logger');
|
|
11
|
+
|
|
12
|
+
// Path selection strategies
|
|
13
|
+
const STRATEGIES = {
|
|
14
|
+
ROUND_ROBIN: 'round-robin',
|
|
15
|
+
LEAST_LATENCY: 'least-latency',
|
|
16
|
+
LOAD_BALANCED: 'load-balanced',
|
|
17
|
+
WEIGHTED: 'weighted'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class MultiPath extends EventEmitter {
|
|
21
|
+
constructor(securityLevel = 'medium', options = {}) {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.securityLevel = securityLevel;
|
|
25
|
+
this.pathCount = this._selectPathCount(securityLevel);
|
|
26
|
+
this.strategy = options.strategy || STRATEGIES.LOAD_BALANCED;
|
|
27
|
+
this.enabled = options.enabled !== false;
|
|
28
|
+
|
|
29
|
+
this.paths = [];
|
|
30
|
+
this.pathStats = [];
|
|
31
|
+
this.currentPathIndex = 0;
|
|
32
|
+
this.sequenceNumber = 0;
|
|
33
|
+
this.receivedPackets = new Map(); // sequence -> packet data
|
|
34
|
+
this.expectedSequence = 0;
|
|
35
|
+
|
|
36
|
+
this.logger = new Logger({ prefix: 'MultiPath' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Select number of paths based on security level
|
|
41
|
+
*/
|
|
42
|
+
_selectPathCount(level) {
|
|
43
|
+
switch (level) {
|
|
44
|
+
case 'low':
|
|
45
|
+
return 2;
|
|
46
|
+
case 'medium':
|
|
47
|
+
return 4;
|
|
48
|
+
case 'high':
|
|
49
|
+
return 5;
|
|
50
|
+
default:
|
|
51
|
+
return 3;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Initialize multiple paths to server
|
|
57
|
+
*/
|
|
58
|
+
async initializePaths(serverHosts, serverPort, connectionOptions = {}) {
|
|
59
|
+
if (!this.enabled) {
|
|
60
|
+
// Single path mode
|
|
61
|
+
const connection = new Connection({
|
|
62
|
+
host: serverHosts[0] || serverHosts,
|
|
63
|
+
port: serverPort,
|
|
64
|
+
...connectionOptions
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await connection.connect();
|
|
68
|
+
this.paths.push(connection);
|
|
69
|
+
this.pathStats.push(this._createPathStats(0));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create multiple connections
|
|
74
|
+
const hosts = Array.isArray(serverHosts) ? serverHosts : [serverHosts];
|
|
75
|
+
const promises = [];
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < this.pathCount; i++) {
|
|
78
|
+
// Distribute across multiple hosts or use same host with different connections
|
|
79
|
+
const host = hosts[i % hosts.length];
|
|
80
|
+
|
|
81
|
+
const connection = new Connection({
|
|
82
|
+
host,
|
|
83
|
+
port: serverPort,
|
|
84
|
+
...connectionOptions
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Set up event handlers
|
|
88
|
+
connection.on('tcp:data', (data) => {
|
|
89
|
+
this.pathStats[i].bytesReceived += data.length;
|
|
90
|
+
this.pathStats[i].packetsReceived++;
|
|
91
|
+
this.emit('data', data, i);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
connection.on('udp:data', (data, rinfo) => {
|
|
95
|
+
this.pathStats[i].bytesReceived += data.length;
|
|
96
|
+
this.pathStats[i].packetsReceived++;
|
|
97
|
+
this.emit('data', data, i);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
connection.on('error', (error) => {
|
|
101
|
+
this.pathStats[i].errors++;
|
|
102
|
+
this.pathStats[i].available = false;
|
|
103
|
+
this.logger.warn(`Path ${i} error:`, error.message);
|
|
104
|
+
this.emit('path-error', i, error);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
connection.on('disconnected', () => {
|
|
108
|
+
this.pathStats[i].available = false;
|
|
109
|
+
this.logger.info(`Path ${i} disconnected`);
|
|
110
|
+
this.emit('path-disconnected', i);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
promises.push(connection.connect().then(() => {
|
|
114
|
+
this.paths.push(connection);
|
|
115
|
+
this.pathStats.push(this._createPathStats(i));
|
|
116
|
+
this.logger.info(`Path ${i} established to ${host}:${serverPort}`);
|
|
117
|
+
}).catch(error => {
|
|
118
|
+
this.logger.error(`Failed to establish path ${i}:`, error.message);
|
|
119
|
+
throw error;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Wait for all connections
|
|
124
|
+
await Promise.all(promises);
|
|
125
|
+
|
|
126
|
+
this.logger.info(`Multi-path routing initialized with ${this.paths.length} paths`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create path statistics object
|
|
131
|
+
*/
|
|
132
|
+
_createPathStats(index) {
|
|
133
|
+
return {
|
|
134
|
+
index,
|
|
135
|
+
available: true,
|
|
136
|
+
latency: 0,
|
|
137
|
+
packetsSent: 0,
|
|
138
|
+
packetsReceived: 0,
|
|
139
|
+
bytesSent: 0,
|
|
140
|
+
bytesReceived: 0,
|
|
141
|
+
errors: 0,
|
|
142
|
+
lastUsed: Date.now(),
|
|
143
|
+
score: 100
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Measure path latency
|
|
149
|
+
*/
|
|
150
|
+
async measureLatency(pathIndex) {
|
|
151
|
+
const path = this.paths[pathIndex];
|
|
152
|
+
if (!path || !path.isConnected()) {
|
|
153
|
+
return Infinity;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const start = Date.now();
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Send a keepalive packet and measure response time
|
|
160
|
+
await new Promise((resolve, reject) => {
|
|
161
|
+
const timeout = setTimeout(() => reject(new Error('Timeout')), 5000);
|
|
162
|
+
|
|
163
|
+
path.once('tcp:data', () => {
|
|
164
|
+
clearTimeout(timeout);
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// In production, send actual keepalive packet
|
|
169
|
+
resolve(); // Simulate immediate response
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const latency = Date.now() - start;
|
|
173
|
+
this.pathStats[pathIndex].latency = latency;
|
|
174
|
+
return latency;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.pathStats[pathIndex].latency = Infinity;
|
|
177
|
+
return Infinity;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Calculate path score for load balancing
|
|
183
|
+
*/
|
|
184
|
+
_calculatePathScore(pathIndex) {
|
|
185
|
+
const stats = this.pathStats[pathIndex];
|
|
186
|
+
|
|
187
|
+
if (!stats.available) {
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Score based on latency, packet loss, and load
|
|
192
|
+
const latencyScore = stats.latency > 0 ? 1000 / stats.latency : 100;
|
|
193
|
+
const lossRate = stats.packetsSent > 0
|
|
194
|
+
? 1 - (stats.errors / stats.packetsSent)
|
|
195
|
+
: 1;
|
|
196
|
+
const loadScore = 100 - (stats.packetsSent % 100);
|
|
197
|
+
|
|
198
|
+
const score = (latencyScore * 0.5) + (lossRate * 30) + (loadScore * 0.2);
|
|
199
|
+
stats.score = Math.max(0, Math.min(100, score));
|
|
200
|
+
|
|
201
|
+
return stats.score;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Select next path based on strategy
|
|
206
|
+
*/
|
|
207
|
+
_selectPath() {
|
|
208
|
+
// Filter available paths
|
|
209
|
+
const availablePaths = this.pathStats
|
|
210
|
+
.map((stat, index) => ({ ...stat, index }))
|
|
211
|
+
.filter(stat => stat.available);
|
|
212
|
+
|
|
213
|
+
if (availablePaths.length === 0) {
|
|
214
|
+
throw new Error('No available paths');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (availablePaths.length === 1) {
|
|
218
|
+
return availablePaths[0].index;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let selectedIndex;
|
|
222
|
+
|
|
223
|
+
switch (this.strategy) {
|
|
224
|
+
case STRATEGIES.ROUND_ROBIN:
|
|
225
|
+
selectedIndex = this.currentPathIndex % availablePaths.length;
|
|
226
|
+
this.currentPathIndex = (this.currentPathIndex + 1) % availablePaths.length;
|
|
227
|
+
return availablePaths[selectedIndex].index;
|
|
228
|
+
|
|
229
|
+
case STRATEGIES.LEAST_LATENCY:
|
|
230
|
+
return availablePaths.reduce((min, path) =>
|
|
231
|
+
path.latency < min.latency ? path : min
|
|
232
|
+
).index;
|
|
233
|
+
|
|
234
|
+
case STRATEGIES.LOAD_BALANCED:
|
|
235
|
+
// Calculate scores for all paths
|
|
236
|
+
availablePaths.forEach(path => {
|
|
237
|
+
this._calculatePathScore(path.index);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Select path with highest score
|
|
241
|
+
return availablePaths.reduce((best, path) =>
|
|
242
|
+
path.score > best.score ? path : best
|
|
243
|
+
).index;
|
|
244
|
+
|
|
245
|
+
case STRATEGIES.WEIGHTED:
|
|
246
|
+
// Weighted random selection based on scores
|
|
247
|
+
const totalScore = availablePaths.reduce((sum, path) =>
|
|
248
|
+
sum + this._calculatePathScore(path.index), 0
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
let random = Math.random() * totalScore;
|
|
252
|
+
for (const path of availablePaths) {
|
|
253
|
+
random -= path.score;
|
|
254
|
+
if (random <= 0) {
|
|
255
|
+
return path.index;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return availablePaths[0].index;
|
|
259
|
+
|
|
260
|
+
default:
|
|
261
|
+
return availablePaths[0].index;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Send data across multiple paths
|
|
267
|
+
*/
|
|
268
|
+
async send(data, protocol = 'tcp') {
|
|
269
|
+
if (!this.enabled || this.paths.length === 1) {
|
|
270
|
+
// Single path mode
|
|
271
|
+
const path = this.paths[0];
|
|
272
|
+
if (protocol === 'tcp') {
|
|
273
|
+
return await path.sendTCP(data);
|
|
274
|
+
} else {
|
|
275
|
+
return await path.sendUDP(data);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Select path
|
|
280
|
+
const pathIndex = this._selectPath();
|
|
281
|
+
const path = this.paths[pathIndex];
|
|
282
|
+
const stats = this.pathStats[pathIndex];
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
// Add sequence number for packet reordering
|
|
286
|
+
const sequence = this.sequenceNumber++;
|
|
287
|
+
const packetWithSeq = Buffer.allocUnsafe(4 + data.length);
|
|
288
|
+
packetWithSeq.writeUInt32BE(sequence, 0);
|
|
289
|
+
data.copy(packetWithSeq, 4);
|
|
290
|
+
|
|
291
|
+
// Send via selected path
|
|
292
|
+
if (protocol === 'tcp') {
|
|
293
|
+
await path.sendTCP(packetWithSeq);
|
|
294
|
+
} else {
|
|
295
|
+
await path.sendUDP(packetWithSeq);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
stats.packetsSent++;
|
|
299
|
+
stats.bytesSent += data.length;
|
|
300
|
+
stats.lastUsed = Date.now();
|
|
301
|
+
|
|
302
|
+
return pathIndex;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
stats.errors++;
|
|
305
|
+
this.logger.error(`Failed to send on path ${pathIndex}:`, error.message);
|
|
306
|
+
|
|
307
|
+
// Try failover to another path
|
|
308
|
+
if (this.paths.length > 1) {
|
|
309
|
+
this.logger.info('Attempting failover to another path');
|
|
310
|
+
stats.available = false;
|
|
311
|
+
return await this.send(data, protocol);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Receive and reorder packets
|
|
320
|
+
*/
|
|
321
|
+
receive(data, pathIndex) {
|
|
322
|
+
// Extract sequence number
|
|
323
|
+
if (data.length < 4) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const sequence = data.readUInt32BE(0);
|
|
328
|
+
const payload = data.slice(4);
|
|
329
|
+
|
|
330
|
+
// Store packet
|
|
331
|
+
this.receivedPackets.set(sequence, payload);
|
|
332
|
+
|
|
333
|
+
// Try to deliver in-order packets
|
|
334
|
+
const deliveredPackets = [];
|
|
335
|
+
|
|
336
|
+
while (this.receivedPackets.has(this.expectedSequence)) {
|
|
337
|
+
const packet = this.receivedPackets.get(this.expectedSequence);
|
|
338
|
+
deliveredPackets.push(packet);
|
|
339
|
+
this.receivedPackets.delete(this.expectedSequence);
|
|
340
|
+
this.expectedSequence++;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return deliveredPackets;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get path statistics
|
|
348
|
+
*/
|
|
349
|
+
getPathStats() {
|
|
350
|
+
return this.pathStats.map(stat => ({ ...stat }));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get overall statistics
|
|
355
|
+
*/
|
|
356
|
+
getStats() {
|
|
357
|
+
const totalStats = {
|
|
358
|
+
pathCount: this.paths.length,
|
|
359
|
+
activePaths: this.pathStats.filter(s => s.available).length,
|
|
360
|
+
strategy: this.strategy,
|
|
361
|
+
totalBytesSent: 0,
|
|
362
|
+
totalBytesReceived: 0,
|
|
363
|
+
totalPacketsSent: 0,
|
|
364
|
+
totalPacketsReceived: 0,
|
|
365
|
+
totalErrors: 0
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
for (const stat of this.pathStats) {
|
|
369
|
+
totalStats.totalBytesSent += stat.bytesSent;
|
|
370
|
+
totalStats.totalBytesReceived += stat.bytesReceived;
|
|
371
|
+
totalStats.totalPacketsSent += stat.packetsSent;
|
|
372
|
+
totalStats.totalPacketsReceived += stat.packetsReceived;
|
|
373
|
+
totalStats.totalErrors += stat.errors;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return totalStats;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Close all paths
|
|
381
|
+
*/
|
|
382
|
+
closeAll() {
|
|
383
|
+
for (const path of this.paths) {
|
|
384
|
+
path.disconnect();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this.paths = [];
|
|
388
|
+
this.pathStats = [];
|
|
389
|
+
this.logger.info('All paths closed');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
module.exports = MultiPath;
|
|
394
|
+
module.exports.STRATEGIES = STRATEGIES;
|