@push.rocks/smartproxy 15.0.2 → 16.0.2

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.
Files changed (80) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/index.d.ts +10 -4
  3. package/dist_ts/certificate/index.js +5 -7
  4. package/dist_ts/certificate/models/certificate-types.d.ts +35 -15
  5. package/dist_ts/certificate/providers/cert-provisioner.d.ts +41 -15
  6. package/dist_ts/certificate/providers/cert-provisioner.js +201 -41
  7. package/dist_ts/forwarding/config/forwarding-types.d.ts +40 -76
  8. package/dist_ts/forwarding/config/forwarding-types.js +19 -18
  9. package/dist_ts/forwarding/config/index.d.ts +4 -2
  10. package/dist_ts/forwarding/config/index.js +5 -3
  11. package/dist_ts/forwarding/handlers/base-handler.js +3 -1
  12. package/dist_ts/forwarding/index.d.ts +5 -6
  13. package/dist_ts/forwarding/index.js +3 -3
  14. package/dist_ts/http/models/http-types.js +1 -1
  15. package/dist_ts/http/port80/acme-interfaces.d.ts +30 -0
  16. package/dist_ts/http/port80/acme-interfaces.js +46 -1
  17. package/dist_ts/http/port80/port80-handler.d.ts +17 -2
  18. package/dist_ts/http/port80/port80-handler.js +49 -11
  19. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -61
  20. package/dist_ts/proxies/smart-proxy/models/interfaces.js +5 -4
  21. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +118 -4
  22. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +70 -4
  23. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +193 -43
  24. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -5
  25. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +25 -146
  26. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +7 -0
  27. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +9 -0
  28. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +54 -1
  29. package/dist_ts/proxies/smart-proxy/route-helpers.js +102 -1
  30. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +3 -9
  31. package/dist_ts/proxies/smart-proxy/route-manager.js +3 -115
  32. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +72 -10
  33. package/dist_ts/proxies/smart-proxy/smart-proxy.js +135 -268
  34. package/dist_ts/proxies/smart-proxy/timeout-manager.js +3 -3
  35. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +12 -0
  36. package/dist_ts/proxies/smart-proxy/utils/index.js +19 -0
  37. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +174 -0
  38. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +332 -0
  39. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +51 -0
  40. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +124 -0
  41. package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +131 -0
  42. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +217 -0
  43. package/dist_ts/proxies/smart-proxy/utils/route-utils.d.ts +79 -0
  44. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +266 -0
  45. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +73 -0
  46. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +242 -0
  47. package/package.json +1 -1
  48. package/readme.md +139 -111
  49. package/readme.plan.md +164 -312
  50. package/ts/00_commitinfo_data.ts +1 -1
  51. package/ts/certificate/index.ts +17 -9
  52. package/ts/certificate/models/certificate-types.ts +37 -16
  53. package/ts/certificate/providers/cert-provisioner.ts +247 -54
  54. package/ts/forwarding/config/forwarding-types.ts +79 -107
  55. package/ts/forwarding/config/index.ts +4 -2
  56. package/ts/forwarding/handlers/base-handler.ts +4 -2
  57. package/ts/forwarding/index.ts +3 -2
  58. package/ts/http/models/http-types.ts +0 -1
  59. package/ts/http/port80/acme-interfaces.ts +84 -0
  60. package/ts/http/port80/port80-handler.ts +61 -15
  61. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -64
  62. package/ts/proxies/smart-proxy/models/route-types.ts +152 -22
  63. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +226 -55
  64. package/ts/proxies/smart-proxy/route-connection-handler.ts +36 -205
  65. package/ts/proxies/smart-proxy/route-helpers/index.ts +9 -0
  66. package/ts/proxies/smart-proxy/route-helpers.ts +165 -11
  67. package/ts/proxies/smart-proxy/route-manager.ts +3 -130
  68. package/ts/proxies/smart-proxy/smart-proxy.ts +157 -329
  69. package/ts/proxies/smart-proxy/timeout-manager.ts +2 -2
  70. package/ts/proxies/smart-proxy/utils/index.ts +40 -0
  71. package/ts/proxies/smart-proxy/utils/route-helpers.ts +455 -0
  72. package/ts/proxies/smart-proxy/utils/route-migration-utils.ts +165 -0
  73. package/ts/proxies/smart-proxy/utils/route-patterns.ts +309 -0
  74. package/ts/proxies/smart-proxy/utils/route-utils.ts +330 -0
  75. package/ts/proxies/smart-proxy/utils/route-validators.ts +269 -0
  76. package/ts/forwarding/config/domain-config.ts +0 -28
  77. package/ts/forwarding/config/domain-manager.ts +0 -283
  78. package/ts/proxies/smart-proxy/connection-handler.ts +0 -1240
  79. package/ts/proxies/smart-proxy/port-range-manager.ts +0 -211
  80. /package/ts/proxies/smart-proxy/{domain-config-manager.ts → domain-config-manager.ts.bak} +0 -0
@@ -1,1240 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type {
3
- IConnectionRecord,
4
- IDomainConfig,
5
- ISmartProxyOptions,
6
- } from './models/interfaces.js';
7
- import { ConnectionManager } from './connection-manager.js';
8
- import { SecurityManager } from './security-manager.js';
9
- import { DomainConfigManager } from './domain-config-manager.js';
10
- import { TlsManager } from './tls-manager.js';
11
- import { NetworkProxyBridge } from './network-proxy-bridge.js';
12
- import { TimeoutManager } from './timeout-manager.js';
13
- import { PortRangeManager } from './port-range-manager.js';
14
- import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
15
- import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
16
-
17
- /**
18
- * Handles new connection processing and setup logic
19
- */
20
- export class ConnectionHandler {
21
- constructor(
22
- private settings: ISmartProxyOptions,
23
- private connectionManager: ConnectionManager,
24
- private securityManager: SecurityManager,
25
- private domainConfigManager: DomainConfigManager,
26
- private tlsManager: TlsManager,
27
- private networkProxyBridge: NetworkProxyBridge,
28
- private timeoutManager: TimeoutManager,
29
- private portRangeManager: PortRangeManager
30
- ) {}
31
-
32
- /**
33
- * Handle a new incoming connection
34
- */
35
- public handleConnection(socket: plugins.net.Socket): void {
36
- const remoteIP = socket.remoteAddress || '';
37
- const localPort = socket.localPort || 0;
38
-
39
- // Validate IP against rate limits and connection limits
40
- const ipValidation = this.securityManager.validateIP(remoteIP);
41
- if (!ipValidation.allowed) {
42
- console.log(`Connection rejected from ${remoteIP}: ${ipValidation.reason}`);
43
- socket.end();
44
- socket.destroy();
45
- return;
46
- }
47
-
48
- // Create a new connection record
49
- const record = this.connectionManager.createConnection(socket);
50
- const connectionId = record.id;
51
-
52
- // Apply socket optimizations
53
- socket.setNoDelay(this.settings.noDelay);
54
-
55
- // Apply keep-alive settings if enabled
56
- if (this.settings.keepAlive) {
57
- socket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
58
- record.hasKeepAlive = true;
59
-
60
- // Apply enhanced TCP keep-alive options if enabled
61
- if (this.settings.enableKeepAliveProbes) {
62
- try {
63
- // These are platform-specific and may not be available
64
- if ('setKeepAliveProbes' in socket) {
65
- (socket as any).setKeepAliveProbes(10);
66
- }
67
- if ('setKeepAliveInterval' in socket) {
68
- (socket as any).setKeepAliveInterval(1000);
69
- }
70
- } catch (err) {
71
- // Ignore errors - these are optional enhancements
72
- if (this.settings.enableDetailedLogging) {
73
- console.log(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`);
74
- }
75
- }
76
- }
77
- }
78
-
79
- if (this.settings.enableDetailedLogging) {
80
- console.log(
81
- `[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` +
82
- `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
83
- `Active connections: ${this.connectionManager.getConnectionCount()}`
84
- );
85
- } else {
86
- console.log(
87
- `New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`
88
- );
89
- }
90
-
91
- // Check if this connection should be forwarded directly to NetworkProxy
92
- if (this.portRangeManager.shouldUseNetworkProxy(localPort)) {
93
- this.handleNetworkProxyConnection(socket, record);
94
- } else {
95
- // For non-NetworkProxy ports, proceed with normal processing
96
- this.handleStandardConnection(socket, record);
97
- }
98
- }
99
-
100
- /**
101
- * Handle a connection that should be forwarded to NetworkProxy
102
- */
103
- private handleNetworkProxyConnection(
104
- socket: plugins.net.Socket,
105
- record: IConnectionRecord
106
- ): void {
107
- const connectionId = record.id;
108
- let initialDataReceived = false;
109
-
110
- // Set an initial timeout for handshake data
111
- let initialTimeout: NodeJS.Timeout | null = setTimeout(() => {
112
- if (!initialDataReceived) {
113
- console.log(
114
- `[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}`
115
- );
116
-
117
- // Add a grace period instead of immediate termination
118
- setTimeout(() => {
119
- if (!initialDataReceived) {
120
- console.log(`[${connectionId}] Final initial data timeout after grace period`);
121
- if (record.incomingTerminationReason === null) {
122
- record.incomingTerminationReason = 'initial_timeout';
123
- this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
124
- }
125
- socket.end();
126
- this.connectionManager.cleanupConnection(record, 'initial_timeout');
127
- }
128
- }, 30000); // 30 second grace period
129
- }
130
- }, this.settings.initialDataTimeout!);
131
-
132
- // Make sure timeout doesn't keep the process alive
133
- if (initialTimeout.unref) {
134
- initialTimeout.unref();
135
- }
136
-
137
- // Set up error handler
138
- socket.on('error', this.connectionManager.handleError('incoming', record));
139
-
140
- // First data handler to capture initial TLS handshake for NetworkProxy
141
- socket.once('data', (chunk: Buffer) => {
142
- // Clear the initial timeout since we've received data
143
- if (initialTimeout) {
144
- clearTimeout(initialTimeout);
145
- initialTimeout = null;
146
- }
147
-
148
- initialDataReceived = true;
149
- record.hasReceivedInitialData = true;
150
-
151
- // Block non-TLS connections on port 443
152
- const localPort = record.localPort;
153
- if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
154
- console.log(
155
- `[${connectionId}] Non-TLS connection detected on port 443. ` +
156
- `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
157
- );
158
- if (record.incomingTerminationReason === null) {
159
- record.incomingTerminationReason = 'non_tls_blocked';
160
- this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
161
- }
162
- socket.end();
163
- this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
164
- return;
165
- }
166
-
167
- // Check if this looks like a TLS handshake
168
- if (this.tlsManager.isTlsHandshake(chunk)) {
169
- record.isTLS = true;
170
-
171
- // Check for ClientHello to extract SNI - but don't enforce it for NetworkProxy
172
- if (this.tlsManager.isClientHello(chunk)) {
173
- // Create connection info for SNI extraction
174
- const connInfo = {
175
- sourceIp: record.remoteIP,
176
- sourcePort: socket.remotePort || 0,
177
- destIp: socket.localAddress || '',
178
- destPort: socket.localPort || 0,
179
- };
180
-
181
- // Extract SNI for domain-specific forwarding if available
182
- const serverName = this.tlsManager.extractSNI(chunk, connInfo);
183
-
184
- // For NetworkProxy connections, we'll allow session tickets even without SNI
185
- // We'll only use the serverName if available to determine the specific forwarding
186
- if (serverName) {
187
- // Save domain config and SNI in connection record
188
- const domainConfig = this.domainConfigManager.findDomainConfig(serverName);
189
- record.domainConfig = domainConfig;
190
- record.lockedDomain = serverName;
191
-
192
- // If we have a domain config and it has a forwarding config
193
- if (domainConfig) {
194
- try {
195
- // Get the forwarding type for this domain
196
- const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
197
-
198
- // For TLS termination types, use NetworkProxy
199
- if (forwardingType === 'https-terminate-to-http' ||
200
- forwardingType === 'https-terminate-to-https') {
201
- const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
202
-
203
- if (this.settings.enableDetailedLogging) {
204
- console.log(
205
- `[${connectionId}] Using TLS termination (${forwardingType}) for ${serverName} on port ${networkProxyPort}`
206
- );
207
- }
208
-
209
- // Forward to NetworkProxy with domain-specific port
210
- this.networkProxyBridge.forwardToNetworkProxy(
211
- connectionId,
212
- socket,
213
- record,
214
- chunk,
215
- networkProxyPort,
216
- (reason) => this.connectionManager.initiateCleanupOnce(record, reason)
217
- );
218
- return;
219
- }
220
-
221
- // For HTTPS passthrough, use the forwarding handler directly
222
- if (forwardingType === 'https-passthrough') {
223
- const handler = this.domainConfigManager.getForwardingHandler(domainConfig);
224
-
225
- if (this.settings.enableDetailedLogging) {
226
- console.log(
227
- `[${connectionId}] Using forwarding handler for SNI passthrough to ${serverName}`
228
- );
229
- }
230
-
231
- // Handle the connection using the handler
232
- handler.handleConnection(socket);
233
-
234
- return;
235
- }
236
-
237
- // For HTTP-only, we shouldn't get TLS connections
238
- if (forwardingType === 'http-only') {
239
- console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName}`);
240
- socket.end();
241
- this.connectionManager.cleanupConnection(record, 'wrong_protocol');
242
- return;
243
- }
244
- } catch (err) {
245
- console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
246
- // Fall through to default NetworkProxy handling
247
- }
248
- }
249
- } else if (
250
- this.settings.allowSessionTicket === false &&
251
- this.settings.enableDetailedLogging
252
- ) {
253
- // Log that we're allowing a session resumption without SNI for NetworkProxy
254
- console.log(
255
- `[${connectionId}] Allowing session resumption without SNI for NetworkProxy forwarding`
256
- );
257
- }
258
- }
259
-
260
- // Forward directly to NetworkProxy without domain-specific settings
261
- this.networkProxyBridge.forwardToNetworkProxy(
262
- connectionId,
263
- socket,
264
- record,
265
- chunk,
266
- undefined,
267
- (reason) => this.connectionManager.initiateCleanupOnce(record, reason)
268
- );
269
- } else {
270
- // If not TLS, handle as plain HTTP
271
- console.log(
272
- `[${connectionId}] Non-TLS connection on NetworkProxy port ${record.localPort}`
273
- );
274
-
275
- // Check if we have a domain config based on port
276
- const portBasedDomainConfig = this.domainConfigManager.findDomainConfigForPort(record.localPort);
277
-
278
- if (portBasedDomainConfig) {
279
- try {
280
- // If this domain supports HTTP via a forwarding handler, use it
281
- if (this.domainConfigManager.supportsHttp(portBasedDomainConfig)) {
282
- const handler = this.domainConfigManager.getForwardingHandler(portBasedDomainConfig);
283
-
284
- if (this.settings.enableDetailedLogging) {
285
- console.log(
286
- `[${connectionId}] Using forwarding handler for non-TLS connection to port ${record.localPort}`
287
- );
288
- }
289
-
290
- // Handle the connection using the handler
291
- handler.handleConnection(socket);
292
-
293
- return;
294
- }
295
- } catch (err) {
296
- console.log(`[${connectionId}] Error using forwarding handler for HTTP: ${err}`);
297
- // Fall through to direct connection
298
- }
299
- }
300
-
301
- // Use legacy direct connection as fallback
302
- this.setupDirectConnection(socket, record, undefined, undefined, chunk);
303
- }
304
- });
305
- }
306
-
307
- /**
308
- * Handle a standard (non-NetworkProxy) connection
309
- */
310
- private handleStandardConnection(socket: plugins.net.Socket, record: IConnectionRecord): void {
311
- const connectionId = record.id;
312
- const localPort = record.localPort;
313
-
314
- // Define helpers for rejecting connections
315
- const rejectIncomingConnection = (reason: string, logMessage: string) => {
316
- console.log(`[${connectionId}] ${logMessage}`);
317
- socket.end();
318
- if (record.incomingTerminationReason === null) {
319
- record.incomingTerminationReason = reason;
320
- this.connectionManager.incrementTerminationStat('incoming', reason);
321
- }
322
- this.connectionManager.cleanupConnection(record, reason);
323
- };
324
-
325
- let initialDataReceived = false;
326
-
327
- // Set an initial timeout for SNI data if needed
328
- let initialTimeout: NodeJS.Timeout | null = null;
329
- if (this.settings.sniEnabled) {
330
- initialTimeout = setTimeout(() => {
331
- if (!initialDataReceived) {
332
- console.log(
333
- `[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}`
334
- );
335
-
336
- // Add a grace period instead of immediate termination
337
- setTimeout(() => {
338
- if (!initialDataReceived) {
339
- console.log(`[${connectionId}] Final initial data timeout after grace period`);
340
- if (record.incomingTerminationReason === null) {
341
- record.incomingTerminationReason = 'initial_timeout';
342
- this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
343
- }
344
- socket.end();
345
- this.connectionManager.cleanupConnection(record, 'initial_timeout');
346
- }
347
- }, 30000); // 30 second grace period
348
- }
349
- }, this.settings.initialDataTimeout!);
350
-
351
- // Make sure timeout doesn't keep the process alive
352
- if (initialTimeout.unref) {
353
- initialTimeout.unref();
354
- }
355
- } else {
356
- initialDataReceived = true;
357
- record.hasReceivedInitialData = true;
358
- }
359
-
360
- socket.on('error', this.connectionManager.handleError('incoming', record));
361
-
362
- // Track data for bytes counting
363
- socket.on('data', (chunk: Buffer) => {
364
- record.bytesReceived += chunk.length;
365
- this.timeoutManager.updateActivity(record);
366
-
367
- // Check for TLS handshake if this is the first chunk
368
- if (!record.isTLS && this.tlsManager.isTlsHandshake(chunk)) {
369
- record.isTLS = true;
370
-
371
- if (this.settings.enableTlsDebugLogging) {
372
- console.log(
373
- `[${connectionId}] TLS handshake detected from ${record.remoteIP}, ${chunk.length} bytes`
374
- );
375
- }
376
- }
377
- });
378
-
379
- /**
380
- * Sets up the connection to the target host.
381
- */
382
- const setupConnection = (
383
- serverName: string,
384
- initialChunk?: Buffer,
385
- forcedDomain?: IDomainConfig,
386
- overridePort?: number
387
- ) => {
388
- // Clear the initial timeout since we've received data
389
- if (initialTimeout) {
390
- clearTimeout(initialTimeout);
391
- initialTimeout = null;
392
- }
393
-
394
- // Mark that we've received initial data
395
- initialDataReceived = true;
396
- record.hasReceivedInitialData = true;
397
-
398
- // Check if this looks like a TLS handshake
399
- if (initialChunk && this.tlsManager.isTlsHandshake(initialChunk)) {
400
- record.isTLS = true;
401
-
402
- if (this.settings.enableTlsDebugLogging) {
403
- console.log(
404
- `[${connectionId}] TLS handshake detected in setup, ${initialChunk.length} bytes`
405
- );
406
- }
407
- }
408
-
409
- // If a forcedDomain is provided (port-based routing), use it; otherwise, use SNI-based lookup.
410
- const domainConfig = forcedDomain
411
- ? forcedDomain
412
- : serverName
413
- ? this.domainConfigManager.findDomainConfig(serverName)
414
- : undefined;
415
-
416
- // Save domain config in connection record
417
- record.domainConfig = domainConfig;
418
-
419
- // Check if this domain should use NetworkProxy (domain-specific setting)
420
- if (
421
- domainConfig &&
422
- this.domainConfigManager.shouldUseNetworkProxy(domainConfig) &&
423
- this.networkProxyBridge.getNetworkProxy()
424
- ) {
425
- if (this.settings.enableDetailedLogging) {
426
- console.log(`[${connectionId}] Domain ${serverName} is configured to use NetworkProxy`);
427
- }
428
-
429
- const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
430
-
431
- if (initialChunk && record.isTLS) {
432
- // For TLS connections with initial chunk, forward to NetworkProxy
433
- this.networkProxyBridge.forwardToNetworkProxy(
434
- connectionId,
435
- socket,
436
- record,
437
- initialChunk,
438
- networkProxyPort,
439
- (reason) => this.connectionManager.initiateCleanupOnce(record, reason)
440
- );
441
- return; // Skip normal connection setup
442
- }
443
- }
444
-
445
- // IP validation
446
- if (domainConfig) {
447
- const ipRules = this.domainConfigManager.getEffectiveIPRules(domainConfig);
448
-
449
- // Perform IP validation using security rules
450
- if (
451
- !this.securityManager.isIPAuthorized(
452
- record.remoteIP,
453
- ipRules.allowedIPs,
454
- ipRules.blockedIPs
455
- )
456
- ) {
457
- return rejectIncomingConnection(
458
- 'rejected',
459
- `Connection rejected: IP ${
460
- record.remoteIP
461
- } not allowed for domain ${domainConfig.domains.join(', ')}`
462
- );
463
- }
464
- } else if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
465
- if (
466
- !this.securityManager.isIPAuthorized(
467
- record.remoteIP,
468
- this.settings.defaultAllowedIPs,
469
- this.settings.defaultBlockedIPs || []
470
- )
471
- ) {
472
- return rejectIncomingConnection(
473
- 'rejected',
474
- `Connection rejected: IP ${record.remoteIP} not allowed by default allowed list`
475
- );
476
- }
477
- }
478
-
479
- // Save the initial SNI
480
- if (serverName) {
481
- record.lockedDomain = serverName;
482
- }
483
-
484
- // Set up the direct connection
485
- this.setupDirectConnection(
486
- socket,
487
- record,
488
- domainConfig,
489
- serverName,
490
- initialChunk,
491
- overridePort
492
- );
493
- };
494
-
495
- // --- PORT RANGE-BASED HANDLING ---
496
- // Only apply port-based rules if the incoming port is within one of the global port ranges.
497
- if (this.portRangeManager.isPortInGlobalRanges(localPort)) {
498
- if (this.portRangeManager.shouldUseGlobalForwarding(localPort)) {
499
- // Create a virtual domain config for global forwarding with security settings
500
- const globalDomainConfig = {
501
- domains: ['global'],
502
- forwarding: {
503
- type: 'http-only' as TForwardingType,
504
- target: {
505
- host: this.settings.targetIP!,
506
- port: this.settings.toPort
507
- },
508
- security: {
509
- allowedIps: this.settings.defaultAllowedIPs || [],
510
- blockedIps: this.settings.defaultBlockedIPs || []
511
- }
512
- },
513
- };
514
-
515
- // Use the same IP filtering mechanism as domain-specific configs
516
- const ipRules = this.domainConfigManager.getEffectiveIPRules(globalDomainConfig);
517
-
518
- if (
519
- !this.securityManager.isIPAuthorized(
520
- record.remoteIP,
521
- ipRules.allowedIPs,
522
- ipRules.blockedIPs
523
- )
524
- ) {
525
- console.log(
526
- `[${connectionId}] Connection from ${record.remoteIP} rejected: IP ${record.remoteIP} not allowed in global default allowed list.`
527
- );
528
- socket.end();
529
- return;
530
- }
531
-
532
- if (this.settings.enableDetailedLogging) {
533
- console.log(
534
- `[${connectionId}] Port-based connection from ${record.remoteIP} on port ${localPort} forwarded to global target IP ${this.settings.targetIP}.`
535
- );
536
- }
537
-
538
- setupConnection('', undefined, globalDomainConfig, localPort);
539
- return;
540
- } else {
541
- // Attempt to find a matching forced domain config based on the local port.
542
- const forcedDomain = this.domainConfigManager.findDomainConfigForPort(localPort);
543
-
544
- if (forcedDomain) {
545
- // Get effective IP rules from the domain config's forwarding security settings
546
- const ipRules = this.domainConfigManager.getEffectiveIPRules(forcedDomain);
547
-
548
- if (
549
- !this.securityManager.isIPAuthorized(
550
- record.remoteIP,
551
- ipRules.allowedIPs,
552
- ipRules.blockedIPs
553
- )
554
- ) {
555
- console.log(
556
- `[${connectionId}] Connection from ${
557
- record.remoteIP
558
- } rejected: IP not allowed for domain ${forcedDomain.domains.join(
559
- ', '
560
- )} on port ${localPort}.`
561
- );
562
- socket.end();
563
- return;
564
- }
565
-
566
- if (this.settings.enableDetailedLogging) {
567
- console.log(
568
- `[${connectionId}] Port-based connection from ${
569
- record.remoteIP
570
- } on port ${localPort} matched domain ${forcedDomain.domains.join(', ')}.`
571
- );
572
- }
573
-
574
- setupConnection('', undefined, forcedDomain, localPort);
575
- return;
576
- }
577
- // Fall through to SNI/default handling if no forced domain config is found.
578
- }
579
- }
580
-
581
- // --- FALLBACK: SNI-BASED HANDLING (or default when SNI is disabled) ---
582
- if (this.settings.sniEnabled) {
583
- initialDataReceived = false;
584
-
585
- socket.once('data', (chunk: Buffer) => {
586
- // Clear timeout immediately
587
- if (initialTimeout) {
588
- clearTimeout(initialTimeout);
589
- initialTimeout = null;
590
- }
591
-
592
- initialDataReceived = true;
593
-
594
- // Block non-TLS connections on port 443
595
- if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
596
- console.log(
597
- `[${connectionId}] Non-TLS connection detected on port 443 in SNI handler. ` +
598
- `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
599
- );
600
- if (record.incomingTerminationReason === null) {
601
- record.incomingTerminationReason = 'non_tls_blocked';
602
- this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
603
- }
604
- socket.end();
605
- this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
606
- return;
607
- }
608
-
609
- // Try to extract SNI
610
- let serverName = '';
611
-
612
- if (this.tlsManager.isTlsHandshake(chunk)) {
613
- record.isTLS = true;
614
-
615
- if (this.settings.enableTlsDebugLogging) {
616
- console.log(
617
- `[${connectionId}] Extracting SNI from TLS handshake, ${chunk.length} bytes`
618
- );
619
- }
620
-
621
- // Create connection info object for SNI extraction
622
- const connInfo = {
623
- sourceIp: record.remoteIP,
624
- sourcePort: socket.remotePort || 0,
625
- destIp: socket.localAddress || '',
626
- destPort: socket.localPort || 0,
627
- };
628
-
629
- // Extract SNI
630
- serverName = this.tlsManager.extractSNI(chunk, connInfo) || '';
631
-
632
- // If allowSessionTicket is false and this is a ClientHello with no SNI, terminate the connection
633
- if (
634
- this.settings.allowSessionTicket === false &&
635
- this.tlsManager.isClientHello(chunk) &&
636
- !serverName
637
- ) {
638
- // Missing SNI: forward to NetworkProxy if available
639
- const proxyInstance = this.networkProxyBridge.getNetworkProxy();
640
- if (proxyInstance) {
641
- if (this.settings.enableDetailedLogging) {
642
- console.log(
643
- `[${connectionId}] No SNI in ClientHello; forwarding to NetworkProxy.`
644
- );
645
- }
646
- this.networkProxyBridge.forwardToNetworkProxy(
647
- connectionId,
648
- socket,
649
- record,
650
- chunk,
651
- undefined,
652
- (_reason) => {
653
- // On proxy failure, send TLS unrecognized_name alert and cleanup
654
- if (record.incomingTerminationReason === null) {
655
- record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
656
- this.connectionManager.incrementTerminationStat(
657
- 'incoming',
658
- 'session_ticket_blocked_no_sni'
659
- );
660
- }
661
- const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
662
- try { socket.cork(); socket.write(alert); socket.uncork(); socket.end(); }
663
- catch { socket.end(); }
664
- this.connectionManager.initiateCleanupOnce(record, 'session_ticket_blocked_no_sni');
665
- }
666
- );
667
- return;
668
- }
669
- // Fallback: send TLS unrecognized_name alert and terminate
670
- console.log(
671
- `[${connectionId}] No SNI detected and proxy unavailable; sending TLS alert.`
672
- );
673
- if (record.incomingTerminationReason === null) {
674
- record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
675
- this.connectionManager.incrementTerminationStat(
676
- 'incoming',
677
- 'session_ticket_blocked_no_sni'
678
- );
679
- }
680
- const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
681
- try { socket.cork(); socket.write(alert); socket.uncork(); socket.end(); }
682
- catch { socket.end(); }
683
- this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
684
- return;
685
- }
686
- }
687
-
688
- // Lock the connection to the negotiated SNI.
689
- record.lockedDomain = serverName;
690
-
691
- if (this.settings.enableDetailedLogging) {
692
- console.log(
693
- `[${connectionId}] Received connection from ${record.remoteIP} with SNI: ${
694
- serverName || '(empty)'
695
- }`
696
- );
697
- }
698
-
699
- setupConnection(serverName, chunk);
700
- });
701
- } else {
702
- initialDataReceived = true;
703
- record.hasReceivedInitialData = true;
704
-
705
- // Create default security settings for non-SNI connections
706
- const defaultSecurity = {
707
- allowedIPs: this.settings.defaultAllowedIPs || [],
708
- blockedIPs: this.settings.defaultBlockedIPs || []
709
- };
710
-
711
- if (defaultSecurity.allowedIPs.length > 0 &&
712
- !this.securityManager.isIPAuthorized(
713
- record.remoteIP,
714
- defaultSecurity.allowedIPs,
715
- defaultSecurity.blockedIPs
716
- )
717
- ) {
718
- return rejectIncomingConnection(
719
- 'rejected',
720
- `Connection rejected: IP ${record.remoteIP} not allowed for non-SNI connection`
721
- );
722
- }
723
-
724
- setupConnection('');
725
- }
726
- }
727
-
728
- /**
729
- * Sets up a direct connection to the target
730
- */
731
- private setupDirectConnection(
732
- socket: plugins.net.Socket,
733
- record: IConnectionRecord,
734
- domainConfig?: IDomainConfig,
735
- serverName?: string,
736
- initialChunk?: Buffer,
737
- overridePort?: number
738
- ): void {
739
- const connectionId = record.id;
740
-
741
- // If we have a domain config, try to use a forwarding handler
742
- if (domainConfig) {
743
- try {
744
- // Get the forwarding handler for this domain
745
- const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
746
-
747
- // Check the forwarding type to determine how to handle the connection
748
- const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
749
-
750
- // For TLS connections, handle differently based on forwarding type
751
- if (record.isTLS) {
752
- // For HTTP-only, we shouldn't get TLS connections
753
- if (forwardingType === 'http-only') {
754
- console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName || 'unknown'}`);
755
- socket.end();
756
- this.connectionManager.initiateCleanupOnce(record, 'wrong_protocol');
757
- return;
758
- }
759
-
760
- // For HTTPS passthrough, use the handler's connection handling
761
- if (forwardingType === 'https-passthrough') {
762
- // If there's initial data, process it first
763
- if (initialChunk) {
764
- record.bytesReceived += initialChunk.length;
765
- }
766
-
767
- // Let the handler take over
768
- if (this.settings.enableDetailedLogging) {
769
- console.log(`[${connectionId}] Using forwarding handler for ${forwardingType} connection to ${serverName || 'unknown'}`);
770
- }
771
-
772
- // Pass the connection to the handler
773
- forwardingHandler.handleConnection(socket);
774
-
775
- // Set metadata fields
776
- record.usingNetworkProxy = false;
777
-
778
- // Add connection information to record
779
- if (serverName) {
780
- record.lockedDomain = serverName;
781
- }
782
-
783
- return;
784
- }
785
-
786
- // For TLS termination types, we'll fall through to the legacy connection setup
787
- // because NetworkProxy is used for termination
788
- }
789
- // For non-TLS connections, check if we support HTTP
790
- else if (!record.isTLS && this.domainConfigManager.supportsHttp(domainConfig)) {
791
- // For HTTP handling that the handler supports natively
792
- if (forwardingType === 'http-only' ||
793
- (forwardingType === 'https-terminate-to-http' || forwardingType === 'https-terminate-to-https')) {
794
-
795
- // If there's redirect to HTTPS configured and this is a normal HTTP connection
796
- if (this.domainConfigManager.shouldRedirectToHttps(domainConfig)) {
797
- // We'll let the handler deal with the HTTP request and potential redirect
798
- // Once an HTTP request arrives, it can redirect as needed
799
- }
800
-
801
- // Let the handler take over for HTTP handling
802
- if (this.settings.enableDetailedLogging) {
803
- console.log(`[${connectionId}] Using forwarding handler for HTTP connection to ${serverName || 'unknown'}`);
804
- }
805
-
806
- // Pass the connection to the handler
807
- forwardingHandler.handleConnection(socket);
808
-
809
- // Add connection information to record
810
- if (serverName) {
811
- record.lockedDomain = serverName;
812
- }
813
-
814
- return;
815
- }
816
- }
817
- } catch (err) {
818
- console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
819
- // Fall through to legacy connection handling
820
- }
821
- }
822
-
823
- // If we get here, we'll use legacy connection handling
824
-
825
- // Determine target host
826
- const targetHost = domainConfig
827
- ? this.domainConfigManager.getTargetIP(domainConfig)
828
- : this.settings.targetIP!;
829
-
830
- // Determine target port - first try forwarding config, then fallback
831
- const targetPort = domainConfig
832
- ? this.domainConfigManager.getTargetPort(domainConfig, overridePort !== undefined ? overridePort : this.settings.toPort)
833
- : (overridePort !== undefined ? overridePort : this.settings.toPort);
834
-
835
- // Setup connection options
836
- const connectionOptions: plugins.net.NetConnectOpts = {
837
- host: targetHost,
838
- port: targetPort,
839
- };
840
-
841
- // Preserve source IP if configured
842
- if (this.settings.preserveSourceIP) {
843
- connectionOptions.localAddress = record.remoteIP.replace('::ffff:', '');
844
- }
845
-
846
- // Create a safe queue for incoming data
847
- const dataQueue: Buffer[] = [];
848
- let queueSize = 0;
849
- let processingQueue = false;
850
- let drainPending = false;
851
- let pipingEstablished = false;
852
-
853
- // Pause the incoming socket to prevent buffer overflows
854
- socket.pause();
855
-
856
- // Function to safely process the data queue without losing events
857
- const processDataQueue = () => {
858
- if (processingQueue || dataQueue.length === 0 || pipingEstablished) return;
859
-
860
- processingQueue = true;
861
-
862
- try {
863
- // Process all queued chunks with the current active handler
864
- while (dataQueue.length > 0) {
865
- const chunk = dataQueue.shift()!;
866
- queueSize -= chunk.length;
867
-
868
- // Once piping is established, we shouldn't get here,
869
- // but just in case, pass to the outgoing socket directly
870
- if (pipingEstablished && record.outgoing) {
871
- record.outgoing.write(chunk);
872
- continue;
873
- }
874
-
875
- // Track bytes received
876
- record.bytesReceived += chunk.length;
877
-
878
- // Check for TLS handshake
879
- if (!record.isTLS && this.tlsManager.isTlsHandshake(chunk)) {
880
- record.isTLS = true;
881
-
882
- if (this.settings.enableTlsDebugLogging) {
883
- console.log(
884
- `[${connectionId}] TLS handshake detected in tempDataHandler, ${chunk.length} bytes`
885
- );
886
- }
887
- }
888
-
889
- // Check if adding this chunk would exceed the buffer limit
890
- const newSize = record.pendingDataSize + chunk.length;
891
-
892
- if (this.settings.maxPendingDataSize && newSize > this.settings.maxPendingDataSize) {
893
- console.log(
894
- `[${connectionId}] Buffer limit exceeded for connection from ${record.remoteIP}: ${newSize} bytes > ${this.settings.maxPendingDataSize} bytes`
895
- );
896
- socket.end(); // Gracefully close the socket
897
- this.connectionManager.initiateCleanupOnce(record, 'buffer_limit_exceeded');
898
- return;
899
- }
900
-
901
- // Buffer the chunk and update the size counter
902
- record.pendingData.push(Buffer.from(chunk));
903
- record.pendingDataSize = newSize;
904
- this.timeoutManager.updateActivity(record);
905
- }
906
- } finally {
907
- processingQueue = false;
908
-
909
- // If there's a pending drain and we've processed everything,
910
- // signal we're ready for more data if we haven't established piping yet
911
- if (drainPending && dataQueue.length === 0 && !pipingEstablished) {
912
- drainPending = false;
913
- socket.resume();
914
- }
915
- }
916
- };
917
-
918
- // Unified data handler that safely queues incoming data
919
- const safeDataHandler = (chunk: Buffer) => {
920
- // If piping is already established, just let the pipe handle it
921
- if (pipingEstablished) return;
922
-
923
- // Add to our queue for orderly processing
924
- dataQueue.push(Buffer.from(chunk)); // Make a copy to be safe
925
- queueSize += chunk.length;
926
-
927
- // If queue is getting large, pause socket until we catch up
928
- if (this.settings.maxPendingDataSize && queueSize > this.settings.maxPendingDataSize * 0.8) {
929
- socket.pause();
930
- drainPending = true;
931
- }
932
-
933
- // Process the queue
934
- processDataQueue();
935
- };
936
-
937
- // Add our safe data handler
938
- socket.on('data', safeDataHandler);
939
-
940
- // Add initial chunk to pending data if present
941
- if (initialChunk) {
942
- record.bytesReceived += initialChunk.length;
943
- record.pendingData.push(Buffer.from(initialChunk));
944
- record.pendingDataSize = initialChunk.length;
945
- }
946
-
947
- // Create the target socket but don't set up piping immediately
948
- const targetSocket = plugins.net.connect(connectionOptions);
949
- record.outgoing = targetSocket;
950
- record.outgoingStartTime = Date.now();
951
-
952
- // Apply socket optimizations
953
- targetSocket.setNoDelay(this.settings.noDelay);
954
-
955
- // Apply keep-alive settings to the outgoing connection as well
956
- if (this.settings.keepAlive) {
957
- targetSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
958
-
959
- // Apply enhanced TCP keep-alive options if enabled
960
- if (this.settings.enableKeepAliveProbes) {
961
- try {
962
- if ('setKeepAliveProbes' in targetSocket) {
963
- (targetSocket as any).setKeepAliveProbes(10);
964
- }
965
- if ('setKeepAliveInterval' in targetSocket) {
966
- (targetSocket as any).setKeepAliveInterval(1000);
967
- }
968
- } catch (err) {
969
- // Ignore errors - these are optional enhancements
970
- if (this.settings.enableDetailedLogging) {
971
- console.log(
972
- `[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}`
973
- );
974
- }
975
- }
976
- }
977
- }
978
-
979
- // Setup specific error handler for connection phase
980
- targetSocket.once('error', (err) => {
981
- // This handler runs only once during the initial connection phase
982
- const code = (err as any).code;
983
- console.log(
984
- `[${connectionId}] Connection setup error to ${targetHost}:${connectionOptions.port}: ${err.message} (${code})`
985
- );
986
-
987
- // Resume the incoming socket to prevent it from hanging
988
- socket.resume();
989
-
990
- if (code === 'ECONNREFUSED') {
991
- console.log(
992
- `[${connectionId}] Target ${targetHost}:${connectionOptions.port} refused connection`
993
- );
994
- } else if (code === 'ETIMEDOUT') {
995
- console.log(
996
- `[${connectionId}] Connection to ${targetHost}:${connectionOptions.port} timed out`
997
- );
998
- } else if (code === 'ECONNRESET') {
999
- console.log(
1000
- `[${connectionId}] Connection to ${targetHost}:${connectionOptions.port} was reset`
1001
- );
1002
- } else if (code === 'EHOSTUNREACH') {
1003
- console.log(`[${connectionId}] Host ${targetHost} is unreachable`);
1004
- }
1005
-
1006
- // Clear any existing error handler after connection phase
1007
- targetSocket.removeAllListeners('error');
1008
-
1009
- // Re-add the normal error handler for established connections
1010
- targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
1011
-
1012
- if (record.outgoingTerminationReason === null) {
1013
- record.outgoingTerminationReason = 'connection_failed';
1014
- this.connectionManager.incrementTerminationStat('outgoing', 'connection_failed');
1015
- }
1016
-
1017
- // If we have a forwarding handler for this domain, let it handle the error
1018
- if (domainConfig) {
1019
- try {
1020
- const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
1021
- forwardingHandler.emit('connection_error', {
1022
- socket,
1023
- error: err,
1024
- connectionId
1025
- });
1026
- } catch (handlerErr) {
1027
- // If getting the handler fails, just log and continue with normal cleanup
1028
- console.log(`Error getting forwarding handler for error handling: ${handlerErr}`);
1029
- }
1030
- }
1031
-
1032
- // Clean up the connection
1033
- this.connectionManager.initiateCleanupOnce(record, `connection_failed_${code}`);
1034
- });
1035
-
1036
- // Setup close handler
1037
- targetSocket.on('close', this.connectionManager.handleClose('outgoing', record));
1038
- socket.on('close', this.connectionManager.handleClose('incoming', record));
1039
-
1040
- // Handle timeouts with keep-alive awareness
1041
- socket.on('timeout', () => {
1042
- // For keep-alive connections, just log a warning instead of closing
1043
- if (record.hasKeepAlive) {
1044
- console.log(
1045
- `[${connectionId}] Timeout event on incoming keep-alive connection from ${
1046
- record.remoteIP
1047
- } after ${plugins.prettyMs(
1048
- this.settings.socketTimeout || 3600000
1049
- )}. Connection preserved.`
1050
- );
1051
- return;
1052
- }
1053
-
1054
- // For non-keep-alive connections, proceed with normal cleanup
1055
- console.log(
1056
- `[${connectionId}] Timeout on incoming side from ${
1057
- record.remoteIP
1058
- } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`
1059
- );
1060
- if (record.incomingTerminationReason === null) {
1061
- record.incomingTerminationReason = 'timeout';
1062
- this.connectionManager.incrementTerminationStat('incoming', 'timeout');
1063
- }
1064
- this.connectionManager.initiateCleanupOnce(record, 'timeout_incoming');
1065
- });
1066
-
1067
- targetSocket.on('timeout', () => {
1068
- // For keep-alive connections, just log a warning instead of closing
1069
- if (record.hasKeepAlive) {
1070
- console.log(
1071
- `[${connectionId}] Timeout event on outgoing keep-alive connection from ${
1072
- record.remoteIP
1073
- } after ${plugins.prettyMs(
1074
- this.settings.socketTimeout || 3600000
1075
- )}. Connection preserved.`
1076
- );
1077
- return;
1078
- }
1079
-
1080
- // For non-keep-alive connections, proceed with normal cleanup
1081
- console.log(
1082
- `[${connectionId}] Timeout on outgoing side from ${
1083
- record.remoteIP
1084
- } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`
1085
- );
1086
- if (record.outgoingTerminationReason === null) {
1087
- record.outgoingTerminationReason = 'timeout';
1088
- this.connectionManager.incrementTerminationStat('outgoing', 'timeout');
1089
- }
1090
- this.connectionManager.initiateCleanupOnce(record, 'timeout_outgoing');
1091
- });
1092
-
1093
- // Apply socket timeouts
1094
- this.timeoutManager.applySocketTimeouts(record);
1095
-
1096
- // Track outgoing data for bytes counting
1097
- targetSocket.on('data', (chunk: Buffer) => {
1098
- record.bytesSent += chunk.length;
1099
- this.timeoutManager.updateActivity(record);
1100
- });
1101
-
1102
- // Wait for the outgoing connection to be ready before setting up piping
1103
- targetSocket.once('connect', () => {
1104
- // Clear the initial connection error handler
1105
- targetSocket.removeAllListeners('error');
1106
-
1107
- // Add the normal error handler for established connections
1108
- targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
1109
-
1110
- // Process any remaining data in the queue before switching to piping
1111
- processDataQueue();
1112
-
1113
- // Set up piping immediately
1114
- pipingEstablished = true;
1115
-
1116
- // Flush all pending data to target
1117
- if (record.pendingData.length > 0) {
1118
- const combinedData = Buffer.concat(record.pendingData);
1119
-
1120
- if (this.settings.enableDetailedLogging) {
1121
- console.log(
1122
- `[${connectionId}] Forwarding ${combinedData.length} bytes of initial data to target`
1123
- );
1124
- }
1125
-
1126
- // Write pending data immediately
1127
- targetSocket.write(combinedData, (err) => {
1128
- if (err) {
1129
- console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`);
1130
- return this.connectionManager.initiateCleanupOnce(record, 'write_error');
1131
- }
1132
- });
1133
-
1134
- // Clear the buffer now that we've processed it
1135
- record.pendingData = [];
1136
- record.pendingDataSize = 0;
1137
- }
1138
-
1139
- // Setup piping in both directions without any delays
1140
- socket.pipe(targetSocket);
1141
- targetSocket.pipe(socket);
1142
-
1143
- // Resume the socket to ensure data flows
1144
- socket.resume();
1145
-
1146
- // Process any data that might be queued in the interim
1147
- if (dataQueue.length > 0) {
1148
- // Write any remaining queued data directly to the target socket
1149
- for (const chunk of dataQueue) {
1150
- targetSocket.write(chunk);
1151
- }
1152
- // Clear the queue
1153
- dataQueue.length = 0;
1154
- queueSize = 0;
1155
- }
1156
-
1157
- if (this.settings.enableDetailedLogging) {
1158
- console.log(
1159
- `[${connectionId}] Connection established: ${record.remoteIP} -> ${targetHost}:${connectionOptions.port}` +
1160
- `${
1161
- serverName
1162
- ? ` (SNI: ${serverName})`
1163
- : domainConfig
1164
- ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
1165
- : ''
1166
- }` +
1167
- ` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
1168
- record.hasKeepAlive ? 'Yes' : 'No'
1169
- }`
1170
- );
1171
- } else {
1172
- console.log(
1173
- `Connection established: ${record.remoteIP} -> ${targetHost}:${connectionOptions.port}` +
1174
- `${
1175
- serverName
1176
- ? ` (SNI: ${serverName})`
1177
- : domainConfig
1178
- ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
1179
- : ''
1180
- }`
1181
- );
1182
- }
1183
-
1184
- // Add the renegotiation handler for SNI validation
1185
- if (serverName) {
1186
- // Create connection info object for the existing connection
1187
- const connInfo = {
1188
- sourceIp: record.remoteIP,
1189
- sourcePort: record.incoming.remotePort || 0,
1190
- destIp: record.incoming.localAddress || '',
1191
- destPort: record.incoming.localPort || 0,
1192
- };
1193
-
1194
- // Create a renegotiation handler function
1195
- const renegotiationHandler = this.tlsManager.createRenegotiationHandler(
1196
- connectionId,
1197
- serverName,
1198
- connInfo,
1199
- (connectionId, reason) => this.connectionManager.initiateCleanupOnce(record, reason)
1200
- );
1201
-
1202
- // Store the handler in the connection record so we can remove it during cleanup
1203
- record.renegotiationHandler = renegotiationHandler;
1204
-
1205
- // Add the handler to the socket
1206
- socket.on('data', renegotiationHandler);
1207
-
1208
- if (this.settings.enableDetailedLogging) {
1209
- console.log(
1210
- `[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`
1211
- );
1212
- if (this.settings.allowSessionTicket === false) {
1213
- console.log(
1214
- `[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`
1215
- );
1216
- }
1217
- }
1218
- }
1219
-
1220
- // Set connection timeout
1221
- record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
1222
- console.log(
1223
- `[${connectionId}] Connection from ${record.remoteIP} exceeded max lifetime, forcing cleanup.`
1224
- );
1225
- this.connectionManager.initiateCleanupOnce(record, reason);
1226
- });
1227
-
1228
- // Mark TLS handshake as complete for TLS connections
1229
- if (record.isTLS) {
1230
- record.tlsHandshakeComplete = true;
1231
-
1232
- if (this.settings.enableTlsDebugLogging) {
1233
- console.log(
1234
- `[${connectionId}] TLS handshake complete for connection from ${record.remoteIP}`
1235
- );
1236
- }
1237
- }
1238
- });
1239
- }
1240
- }