@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.
@@ -0,0 +1,544 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Yaksha VPN Client
5
+ * High-performance VPN client with automatic reconnection and all features
6
+ */
7
+
8
+ const EventEmitter = require('events');
9
+ const Connection = require('../core/connection');
10
+ const Protocol = require('../core/protocol');
11
+ const Encryption = require('../core/encryption');
12
+ const SecurityLevels = require('../security/levels');
13
+ const SNISpoofing = require('../features/sni-spoof');
14
+ const TrafficObfuscation = require('../features/traffic-obfuscation');
15
+ const MultiPath = require('../features/multi-path');
16
+ const DNSOverride = require('../features/dns-override');
17
+ const TLSCamouflage = require('../features/tls-camouflage');
18
+ const FirewallEvasion = require('../bypass/firewall-evasion');
19
+ const Config = require('../utils/config');
20
+ const Logger = require('../utils/logger');
21
+
22
+ class YakshaClient extends EventEmitter {
23
+ constructor(options = {}) {
24
+ super();
25
+
26
+ // Configuration
27
+ this.config = new Config(options);
28
+ this.serverHost = options.server || 'localhost';
29
+ this.serverPort = options.port || 8443;
30
+ this.securityLevel = options.securityLevel || 'medium';
31
+ this.autoReconnect = options.autoReconnect !== false;
32
+ this.authCredentials = options.authCredentials || {};
33
+ this.customSecurityConfig = options.customSecurityConfig || null;
34
+
35
+ // Security configuration
36
+ if (this.securityLevel === 'custom' && this.customSecurityConfig) {
37
+ this.securityConfig = SecurityLevels.createCustomConfig(
38
+ this.customSecurityConfig,
39
+ this.customSecurityConfig.baseLevel || 'medium'
40
+ );
41
+ } else {
42
+ this.securityConfig = SecurityLevels.getConfig(this.securityLevel);
43
+ }
44
+
45
+ // Core components
46
+ this.protocol = new Protocol({
47
+ maxPayloadSize: options.maxPayloadSize || 65535,
48
+ enablePadding: this.securityConfig.obfuscation !== 'none'
49
+ });
50
+
51
+ this.encryption = new Encryption(this.securityLevel, {
52
+ customConfig: this.securityLevel === 'custom' ? this.securityConfig : null
53
+ });
54
+
55
+ // Feature modules
56
+ this.features = {
57
+ sniSpoofing: new SNISpoofing(this.securityLevel, {
58
+ enabled: options.features?.sniSpoofing !== false,
59
+ bugHost: options.bugHost || this.securityConfig.bugHost || null
60
+ }),
61
+ obfuscation: new TrafficObfuscation(this.securityLevel, {
62
+ enabled: options.features?.trafficObfuscation !== false
63
+ }),
64
+ multiPath: new MultiPath(this.securityLevel, {
65
+ enabled: options.features?.multiPath !== false
66
+ }),
67
+ dnsOverride: new DNSOverride(this.securityLevel, {
68
+ enabled: options.features?.dnsOverride !== false
69
+ }),
70
+ tlsCamouflage: new TLSCamouflage(this.securityLevel, {
71
+ enabled: options.features?.tlsCamouflage !== false
72
+ }),
73
+ firewallEvasion: new FirewallEvasion(this.securityLevel, {
74
+ enabled: options.features?.firewallEvasion !== false
75
+ })
76
+ };
77
+
78
+ this.logger = new Logger({
79
+ prefix: 'Client',
80
+ level: options.logLevel || 'info'
81
+ });
82
+
83
+ // Client state
84
+ this.connection = null;
85
+ this.sessionId = null;
86
+ this.sequence = 0;
87
+ this.connected = false;
88
+ this.authenticated = false;
89
+
90
+ // Statistics
91
+ this.stats = {
92
+ connectedAt: null,
93
+ bytesReceived: 0,
94
+ bytesSent: 0,
95
+ packetsReceived: 0,
96
+ packetsSent: 0,
97
+ reconnects: 0,
98
+ errors: 0
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Connect to VPN server
104
+ */
105
+ async connect() {
106
+ if (this.connected) {
107
+ throw new Error('Already connected');
108
+ }
109
+
110
+ this.logger.info(`Connecting to Yaksha VPN Server at ${this.serverHost}:${this.serverPort}`);
111
+ this.logger.info(`Security Level: ${this.securityLevel.toUpperCase()}`);
112
+
113
+ try {
114
+ // Check if multi-path is enabled
115
+ if (this.features.multiPath.enabled) {
116
+ await this._connectMultiPath();
117
+ } else {
118
+ await this._connectSinglePath();
119
+ }
120
+
121
+ // Perform handshake
122
+ await this._performHandshake();
123
+
124
+ this.connected = true;
125
+ this.stats.connectedAt = Date.now();
126
+
127
+ this.emit('connected', {
128
+ server: `${this.serverHost}:${this.serverPort}`,
129
+ sessionId: this.sessionId
130
+ });
131
+
132
+ this.logger.info(`Connected successfully. Session ID: ${this.sessionId}`);
133
+
134
+ // Start keepalive
135
+ this._startKeepalive();
136
+
137
+ } catch (error) {
138
+ this.logger.error('Connection failed:', error.message);
139
+ this.stats.errors++;
140
+ await this.disconnect();
141
+ throw error;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Connect using single path
147
+ */
148
+ async _connectSinglePath() {
149
+ this.connection = new Connection({
150
+ host: this.serverHost,
151
+ port: this.serverPort,
152
+ autoReconnect: this.autoReconnect
153
+ });
154
+
155
+ // Set up event handlers
156
+ this.connection.on('tcp:data', (data) => {
157
+ this._handleData(data);
158
+ });
159
+
160
+ this.connection.on('udp:data', (data) => {
161
+ this._handleData(data);
162
+ });
163
+
164
+ this.connection.on('disconnected', () => {
165
+ this._handleDisconnection();
166
+ });
167
+
168
+ this.connection.on('reconnecting', (attempt, delay) => {
169
+ this.logger.info(`Reconnecting... (attempt ${attempt}, delay ${delay}ms)`);
170
+ this.emit('reconnecting', attempt, delay);
171
+ });
172
+
173
+ this.connection.on('error', (error) => {
174
+ this.logger.error('Connection error:', error.message);
175
+ this.stats.errors++;
176
+ this.emit('error', error);
177
+ });
178
+
179
+ await this.connection.connect();
180
+ }
181
+
182
+ /**
183
+ * Connect using multi-path
184
+ */
185
+ async _connectMultiPath() {
186
+ // Initialize multi-path routing
187
+ await this.features.multiPath.initializePaths(
188
+ this.serverHost,
189
+ this.serverPort,
190
+ {
191
+ autoReconnect: this.autoReconnect
192
+ }
193
+ );
194
+
195
+ // Set up event handlers
196
+ this.features.multiPath.on('data', (data, pathIndex) => {
197
+ this._handleData(data);
198
+ });
199
+
200
+ this.features.multiPath.on('path-error', (pathIndex, error) => {
201
+ this.logger.warn(`Path ${pathIndex} error:`, error.message);
202
+ });
203
+
204
+ this.features.multiPath.on('path-disconnected', (pathIndex) => {
205
+ this.logger.warn(`Path ${pathIndex} disconnected`);
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Perform handshake with server
211
+ */
212
+ async _performHandshake() {
213
+ // Generate encryption keys
214
+ this.encryption.generateKeys();
215
+
216
+ // Build handshake packet
217
+ const handshakeData = JSON.stringify({
218
+ version: '1.1.0',
219
+ clientPublicKey: this.encryption.keys.publicKey
220
+ ? this.encryption.keys.publicKey.toString('base64')
221
+ : null,
222
+ securityLevel: this.securityLevel,
223
+ credentials: this.authCredentials,
224
+ features: {
225
+ sniSpoofing: this.features.sniSpoofing.enabled,
226
+ obfuscation: this.features.obfuscation.enabled,
227
+ multiPath: this.features.multiPath.enabled,
228
+ dnsOverride: this.features.dnsOverride.enabled,
229
+ tlsCamouflage: this.features.tlsCamouflage.enabled,
230
+ firewallEvasion: this.features.firewallEvasion.enabled
231
+ }
232
+ });
233
+
234
+ // Generate temporary session ID
235
+ const tempSessionId = Math.floor(Math.random() * 0xFFFFFFFF);
236
+
237
+ const { packet } = this.protocol.createHandshake(
238
+ tempSessionId,
239
+ this.sequence++,
240
+ Buffer.from(handshakeData, 'utf8')
241
+ );
242
+
243
+ // Send handshake
244
+ await this._sendPacket(packet);
245
+
246
+ // Wait for handshake response
247
+ return new Promise((resolve, reject) => {
248
+ const timeout = setTimeout(() => {
249
+ reject(new Error('Handshake timeout'));
250
+ }, 10000);
251
+
252
+ const handler = (data) => {
253
+ try {
254
+ const { header, payload } = this.protocol.deserialize(data);
255
+
256
+ if (header.type === Protocol.PACKET_TYPES.HANDSHAKE) {
257
+ const response = JSON.parse(payload.toString('utf8'));
258
+
259
+ // Store session ID
260
+ this.sessionId = response.sessionId;
261
+
262
+ // Perform key exchange if server provided public key
263
+ if (response.publicKey) {
264
+ const serverPublicKey = Buffer.from(response.publicKey, 'base64');
265
+ this.encryption.keyExchange(serverPublicKey);
266
+ }
267
+
268
+ // Wait for auth response
269
+ this.once('_authResponse', (authSuccess) => {
270
+ clearTimeout(timeout);
271
+ if (authSuccess) {
272
+ this.authenticated = true;
273
+ resolve();
274
+ } else {
275
+ reject(new Error('Authentication failed'));
276
+ }
277
+ });
278
+ } else if (header.type === Protocol.PACKET_TYPES.DATA) {
279
+ // Authentication response
280
+ const response = JSON.parse(payload.toString('utf8'));
281
+ this.emit('_authResponse', response.success);
282
+ }
283
+ } catch (error) {
284
+ // Ignore parse errors during handshake
285
+ }
286
+ };
287
+
288
+ this.once('_rawData', handler);
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Handle incoming data
294
+ */
295
+ _handleData(data) {
296
+ this.emit('_rawData', data);
297
+
298
+ try {
299
+ const { header, payload } = this.protocol.deserialize(data);
300
+
301
+ this.stats.packetsReceived++;
302
+ this.stats.bytesReceived += data.length;
303
+
304
+ // Handle different packet types
305
+ switch (header.type) {
306
+ case Protocol.PACKET_TYPES.DATA:
307
+ this._handleDataPacket(payload);
308
+ break;
309
+
310
+ case Protocol.PACKET_TYPES.KEEPALIVE:
311
+ // Server keepalive response
312
+ break;
313
+
314
+ case Protocol.PACKET_TYPES.CLOSE:
315
+ const reason = payload.toString('utf8');
316
+ this.logger.info(`Server closing connection: ${reason}`);
317
+ this.disconnect();
318
+ break;
319
+
320
+ default:
321
+ this.logger.warn(`Unknown packet type: ${header.type}`);
322
+ }
323
+
324
+ } catch (error) {
325
+ this.logger.error('Error handling data:', error.message);
326
+ this.stats.errors++;
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Handle data packet
332
+ */
333
+ _handleDataPacket(payload) {
334
+ if (!this.authenticated) {
335
+ return;
336
+ }
337
+
338
+ // Decrypt data if encrypted
339
+ try {
340
+ // In production, decrypt payload here
341
+ // const decrypted = this.encryption.decrypt(...);
342
+
343
+ this.emit('data', payload);
344
+
345
+ } catch (error) {
346
+ this.logger.error('Error handling data packet:', error.message);
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Send data through VPN tunnel
352
+ */
353
+ async send(data, protocol = 'udp') {
354
+ if (!this.connected || !this.authenticated) {
355
+ throw new Error('Not connected or authenticated');
356
+ }
357
+
358
+ try {
359
+ // Apply features in order
360
+
361
+ // 1. TLS Camouflage
362
+ if (this.features.tlsCamouflage.enabled) {
363
+ data = this.features.tlsCamouflage.camouflage(data);
364
+ }
365
+
366
+ // 2. Traffic Obfuscation
367
+ if (this.features.obfuscation.enabled) {
368
+ const { data: obfuscated } = this.features.obfuscation.obfuscate(data);
369
+ data = obfuscated;
370
+ }
371
+
372
+ // 3. Firewall Evasion
373
+ if (this.features.firewallEvasion.enabled) {
374
+ data = this.features.firewallEvasion.evadeDPI(data);
375
+ }
376
+
377
+ // 4. Create protocol packet
378
+ const { packet } = this.protocol.createData(
379
+ this.sessionId,
380
+ this.sequence++,
381
+ data
382
+ );
383
+
384
+ // 5. Send via multi-path or single path
385
+ await this._sendPacket(packet, protocol);
386
+
387
+ this.stats.packetsSent++;
388
+ this.stats.bytesSent += packet.length;
389
+
390
+ } catch (error) {
391
+ this.logger.error('Error sending data:', error.message);
392
+ this.stats.errors++;
393
+ throw error;
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Send packet
399
+ */
400
+ async _sendPacket(packet, protocol = 'tcp') {
401
+ if (this.features.multiPath.enabled && this.features.multiPath.paths.length > 0) {
402
+ await this.features.multiPath.send(packet, protocol);
403
+ } else if (this.connection) {
404
+ if (protocol === 'tcp') {
405
+ await this.connection.sendTCP(packet);
406
+ } else {
407
+ await this.connection.sendUDP(packet);
408
+ }
409
+ } else {
410
+ throw new Error('No connection available');
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Start keepalive mechanism
416
+ */
417
+ _startKeepalive() {
418
+ this.keepaliveInterval = setInterval(() => {
419
+ if (this.connected && this.authenticated) {
420
+ const { packet } = this.protocol.createKeepalive(
421
+ this.sessionId,
422
+ this.sequence++
423
+ );
424
+
425
+ this._sendPacket(packet, 'tcp').catch(error => {
426
+ this.logger.error('Keepalive failed:', error.message);
427
+ });
428
+ }
429
+ }, 30000); // Every 30 seconds
430
+ }
431
+
432
+ /**
433
+ * Stop keepalive mechanism
434
+ */
435
+ _stopKeepalive() {
436
+ if (this.keepaliveInterval) {
437
+ clearInterval(this.keepaliveInterval);
438
+ this.keepaliveInterval = null;
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Handle disconnection
444
+ */
445
+ _handleDisconnection() {
446
+ if (!this.connected) {
447
+ return;
448
+ }
449
+
450
+ this.logger.info('Disconnected from server');
451
+ this.connected = false;
452
+ this.authenticated = false;
453
+
454
+ this._stopKeepalive();
455
+
456
+ this.emit('disconnected');
457
+
458
+ if (this.autoReconnect) {
459
+ this.logger.info('Attempting to reconnect...');
460
+ this.stats.reconnects++;
461
+
462
+ setTimeout(() => {
463
+ this.connect().catch(error => {
464
+ this.logger.error('Reconnection failed:', error.message);
465
+ });
466
+ }, 3000);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Disconnect from server
472
+ */
473
+ async disconnect() {
474
+ if (!this.connected) {
475
+ return;
476
+ }
477
+
478
+ this.logger.info('Disconnecting from server...');
479
+
480
+ try {
481
+ // Send close packet
482
+ const { packet } = this.protocol.createClose(
483
+ this.sessionId,
484
+ this.sequence++,
485
+ 'Client disconnecting'
486
+ );
487
+
488
+ await this._sendPacket(packet, 'tcp');
489
+ } catch (error) {
490
+ // Ignore errors during disconnect
491
+ }
492
+
493
+ this._stopKeepalive();
494
+
495
+ if (this.features.multiPath.enabled) {
496
+ this.features.multiPath.closeAll();
497
+ } else if (this.connection) {
498
+ this.connection.disconnect();
499
+ }
500
+
501
+ this.connected = false;
502
+ this.authenticated = false;
503
+ this.sessionId = null;
504
+
505
+ this.emit('disconnected');
506
+ this.logger.info('Disconnected');
507
+ }
508
+
509
+ /**
510
+ * Get statistics
511
+ */
512
+ getStats() {
513
+ const uptime = this.stats.connectedAt
514
+ ? Date.now() - this.stats.connectedAt
515
+ : 0;
516
+
517
+ const stats = {
518
+ ...this.stats,
519
+ uptime,
520
+ connected: this.connected,
521
+ authenticated: this.authenticated
522
+ };
523
+
524
+ // Add multi-path stats if enabled
525
+ if (this.features.multiPath.enabled) {
526
+ stats.multiPath = this.features.multiPath.getStats();
527
+ }
528
+
529
+ return stats;
530
+ }
531
+
532
+ /**
533
+ * Resolve DNS through VPN tunnel
534
+ */
535
+ async resolveDNS(domain, type = 'A') {
536
+ if (this.features.dnsOverride.enabled) {
537
+ return await this.features.dnsOverride.resolve(domain, type);
538
+ }
539
+
540
+ throw new Error('DNS override not enabled');
541
+ }
542
+ }
543
+
544
+ module.exports = YakshaClient;