@push.rocks/smartproxy 3.30.0 → 3.30.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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.portproxy.js +64 -31
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.portproxy.ts +248 -152
package/ts/classes.portproxy.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface IDomainConfig {
|
|
|
10
10
|
portRanges?: Array<{ from: number; to: number }>; // Optional port ranges
|
|
11
11
|
// Allow domain-specific timeout override
|
|
12
12
|
connectionTimeout?: number; // Connection timeout override (ms)
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
// New properties for NetworkProxy integration
|
|
15
15
|
useNetworkProxy?: boolean; // When true, forwards TLS connections to NetworkProxy
|
|
16
16
|
networkProxyIndex?: number; // Optional index to specify which NetworkProxy to use (defaults to 0)
|
|
@@ -54,12 +54,12 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
|
|
|
54
54
|
// Rate limiting and security
|
|
55
55
|
maxConnectionsPerIP?: number; // Maximum simultaneous connections from a single IP
|
|
56
56
|
connectionRateLimitPerMinute?: number; // Max new connections per minute from a single IP
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
// Enhanced keep-alive settings
|
|
59
59
|
keepAliveTreatment?: 'standard' | 'extended' | 'immortal'; // How to treat keep-alive connections
|
|
60
60
|
keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections
|
|
61
61
|
extendedKeepAliveLifetime?: number; // Extended lifetime for keep-alive connections (ms)
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
// New property for NetworkProxy integration
|
|
64
64
|
networkProxies?: NetworkProxy[]; // Array of NetworkProxy instances to use for TLS termination
|
|
65
65
|
}
|
|
@@ -90,17 +90,17 @@ interface IConnectionRecord {
|
|
|
90
90
|
tlsHandshakeComplete: boolean; // Whether the TLS handshake is complete
|
|
91
91
|
hasReceivedInitialData: boolean; // Whether initial data has been received
|
|
92
92
|
domainConfig?: IDomainConfig; // Associated domain config for this connection
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
// Keep-alive tracking
|
|
95
95
|
hasKeepAlive: boolean; // Whether keep-alive is enabled for this connection
|
|
96
96
|
inactivityWarningIssued?: boolean; // Whether an inactivity warning has been issued
|
|
97
97
|
incomingTerminationReason?: string | null; // Reason for incoming termination
|
|
98
98
|
outgoingTerminationReason?: string | null; // Reason for outgoing termination
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
// New field for NetworkProxy tracking
|
|
101
101
|
usingNetworkProxy?: boolean; // Whether this connection is using a NetworkProxy
|
|
102
102
|
networkProxyIndex?: number; // Which NetworkProxy instance is being used
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
// Sleep detection fields
|
|
105
105
|
possibleSystemSleep?: boolean; // Flag to indicate a possible system sleep was detected
|
|
106
106
|
lastSleepDetection?: number; // Timestamp of the last sleep detection
|
|
@@ -352,7 +352,7 @@ export class PortProxy {
|
|
|
352
352
|
// Connection tracking by IP for rate limiting
|
|
353
353
|
private connectionsByIP: Map<string, Set<string>> = new Map();
|
|
354
354
|
private connectionRateByIP: Map<string, number[]> = new Map();
|
|
355
|
-
|
|
355
|
+
|
|
356
356
|
// New property to store NetworkProxy instances
|
|
357
357
|
private networkProxies: NetworkProxy[] = [];
|
|
358
358
|
|
|
@@ -379,8 +379,8 @@ export class PortProxy {
|
|
|
379
379
|
|
|
380
380
|
// Feature flags
|
|
381
381
|
disableInactivityCheck: settingsArg.disableInactivityCheck || false,
|
|
382
|
-
enableKeepAliveProbes:
|
|
383
|
-
? settingsArg.enableKeepAliveProbes : true, // Enable by default
|
|
382
|
+
enableKeepAliveProbes:
|
|
383
|
+
settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true, // Enable by default
|
|
384
384
|
enableDetailedLogging: settingsArg.enableDetailedLogging || false,
|
|
385
385
|
enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
|
|
386
386
|
enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false, // Disable randomization by default
|
|
@@ -388,13 +388,13 @@ export class PortProxy {
|
|
|
388
388
|
// Rate limiting defaults
|
|
389
389
|
maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100, // 100 connections per IP
|
|
390
390
|
connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300, // 300 per minute
|
|
391
|
-
|
|
391
|
+
|
|
392
392
|
// Enhanced keep-alive settings
|
|
393
393
|
keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended', // Extended by default
|
|
394
394
|
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6, // 6x normal inactivity timeout
|
|
395
395
|
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
396
396
|
};
|
|
397
|
-
|
|
397
|
+
|
|
398
398
|
// Store NetworkProxy instances if provided
|
|
399
399
|
this.networkProxies = settingsArg.networkProxies || [];
|
|
400
400
|
}
|
|
@@ -417,58 +417,66 @@ export class PortProxy {
|
|
|
417
417
|
serverName?: string
|
|
418
418
|
): void {
|
|
419
419
|
// Determine which NetworkProxy to use
|
|
420
|
-
const proxyIndex =
|
|
421
|
-
? domainConfig.networkProxyIndex
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
const proxyIndex =
|
|
421
|
+
domainConfig.networkProxyIndex !== undefined ? domainConfig.networkProxyIndex : 0;
|
|
422
|
+
|
|
424
423
|
// Validate the NetworkProxy index
|
|
425
424
|
if (proxyIndex < 0 || proxyIndex >= this.networkProxies.length) {
|
|
426
|
-
console.log(
|
|
425
|
+
console.log(
|
|
426
|
+
`[${connectionId}] Invalid NetworkProxy index: ${proxyIndex}. Using fallback direct connection.`
|
|
427
|
+
);
|
|
427
428
|
// Fall back to direct connection
|
|
428
|
-
return this.setupDirectConnection(
|
|
429
|
+
return this.setupDirectConnection(
|
|
430
|
+
connectionId,
|
|
431
|
+
socket,
|
|
432
|
+
record,
|
|
433
|
+
domainConfig,
|
|
434
|
+
serverName,
|
|
435
|
+
initialData
|
|
436
|
+
);
|
|
429
437
|
}
|
|
430
|
-
|
|
438
|
+
|
|
431
439
|
const networkProxy = this.networkProxies[proxyIndex];
|
|
432
440
|
const proxyPort = networkProxy.getListeningPort();
|
|
433
441
|
const proxyHost = 'localhost'; // Assuming NetworkProxy runs locally
|
|
434
|
-
|
|
442
|
+
|
|
435
443
|
if (this.settings.enableDetailedLogging) {
|
|
436
444
|
console.log(
|
|
437
445
|
`[${connectionId}] Forwarding TLS connection to NetworkProxy[${proxyIndex}] at ${proxyHost}:${proxyPort}`
|
|
438
446
|
);
|
|
439
447
|
}
|
|
440
|
-
|
|
448
|
+
|
|
441
449
|
// Create a connection to the NetworkProxy
|
|
442
450
|
const proxySocket = plugins.net.connect({
|
|
443
451
|
host: proxyHost,
|
|
444
|
-
port: proxyPort
|
|
452
|
+
port: proxyPort,
|
|
445
453
|
});
|
|
446
|
-
|
|
454
|
+
|
|
447
455
|
// Store the outgoing socket in the record
|
|
448
456
|
record.outgoing = proxySocket;
|
|
449
457
|
record.outgoingStartTime = Date.now();
|
|
450
458
|
record.usingNetworkProxy = true;
|
|
451
459
|
record.networkProxyIndex = proxyIndex;
|
|
452
|
-
|
|
460
|
+
|
|
453
461
|
// Set up error handlers
|
|
454
462
|
proxySocket.on('error', (err) => {
|
|
455
463
|
console.log(`[${connectionId}] Error connecting to NetworkProxy: ${err.message}`);
|
|
456
464
|
this.cleanupConnection(record, 'network_proxy_connect_error');
|
|
457
465
|
});
|
|
458
|
-
|
|
466
|
+
|
|
459
467
|
// Handle connection to NetworkProxy
|
|
460
468
|
proxySocket.on('connect', () => {
|
|
461
469
|
if (this.settings.enableDetailedLogging) {
|
|
462
470
|
console.log(`[${connectionId}] Connected to NetworkProxy at ${proxyHost}:${proxyPort}`);
|
|
463
471
|
}
|
|
464
|
-
|
|
472
|
+
|
|
465
473
|
// First send the initial data that contains the TLS ClientHello
|
|
466
474
|
proxySocket.write(initialData);
|
|
467
|
-
|
|
475
|
+
|
|
468
476
|
// Now set up bidirectional piping between client and NetworkProxy
|
|
469
477
|
socket.pipe(proxySocket);
|
|
470
478
|
proxySocket.pipe(socket);
|
|
471
|
-
|
|
479
|
+
|
|
472
480
|
// Setup cleanup handlers
|
|
473
481
|
proxySocket.on('close', () => {
|
|
474
482
|
if (this.settings.enableDetailedLogging) {
|
|
@@ -476,32 +484,37 @@ export class PortProxy {
|
|
|
476
484
|
}
|
|
477
485
|
this.cleanupConnection(record, 'network_proxy_closed');
|
|
478
486
|
});
|
|
479
|
-
|
|
487
|
+
|
|
480
488
|
socket.on('close', () => {
|
|
481
489
|
if (this.settings.enableDetailedLogging) {
|
|
482
|
-
console.log(
|
|
490
|
+
console.log(
|
|
491
|
+
`[${connectionId}] Client connection closed after forwarding to NetworkProxy`
|
|
492
|
+
);
|
|
483
493
|
}
|
|
484
494
|
this.cleanupConnection(record, 'client_closed');
|
|
485
495
|
});
|
|
486
|
-
|
|
496
|
+
|
|
487
497
|
// Update activity on data transfer
|
|
488
498
|
socket.on('data', (chunk: Buffer) => {
|
|
489
499
|
this.updateActivity(record);
|
|
490
|
-
|
|
500
|
+
|
|
491
501
|
// Check for potential TLS renegotiation or reconnection packets
|
|
492
|
-
if (chunk.length > 0 && chunk[0] === 22) {
|
|
502
|
+
if (chunk.length > 0 && chunk[0] === 22) {
|
|
503
|
+
// ContentType.handshake
|
|
493
504
|
if (this.settings.enableDetailedLogging) {
|
|
494
|
-
console.log(
|
|
505
|
+
console.log(
|
|
506
|
+
`[${connectionId}] Detected potential TLS handshake data while connected to NetworkProxy`
|
|
507
|
+
);
|
|
495
508
|
}
|
|
496
|
-
|
|
509
|
+
|
|
497
510
|
// Let the NetworkProxy handle the TLS renegotiation
|
|
498
511
|
// Just update the activity timestamp to prevent timeouts
|
|
499
512
|
record.lastActivity = Date.now();
|
|
500
513
|
}
|
|
501
514
|
});
|
|
502
|
-
|
|
515
|
+
|
|
503
516
|
proxySocket.on('data', () => this.updateActivity(record));
|
|
504
|
-
|
|
517
|
+
|
|
505
518
|
if (this.settings.enableDetailedLogging) {
|
|
506
519
|
console.log(
|
|
507
520
|
`[${connectionId}] TLS connection successfully forwarded to NetworkProxy[${proxyIndex}]`
|
|
@@ -509,7 +522,7 @@ export class PortProxy {
|
|
|
509
522
|
}
|
|
510
523
|
});
|
|
511
524
|
}
|
|
512
|
-
|
|
525
|
+
|
|
513
526
|
/**
|
|
514
527
|
* Sets up a direct connection to the target (original behavior)
|
|
515
528
|
* This is used when NetworkProxy isn't configured or as a fallback
|
|
@@ -586,11 +599,11 @@ export class PortProxy {
|
|
|
586
599
|
|
|
587
600
|
// Apply socket optimizations
|
|
588
601
|
targetSocket.setNoDelay(this.settings.noDelay);
|
|
589
|
-
|
|
602
|
+
|
|
590
603
|
// Apply keep-alive settings to the outgoing connection as well
|
|
591
604
|
if (this.settings.keepAlive) {
|
|
592
605
|
targetSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
593
|
-
|
|
606
|
+
|
|
594
607
|
// Apply enhanced TCP keep-alive options if enabled
|
|
595
608
|
if (this.settings.enableKeepAliveProbes) {
|
|
596
609
|
try {
|
|
@@ -603,7 +616,9 @@ export class PortProxy {
|
|
|
603
616
|
} catch (err) {
|
|
604
617
|
// Ignore errors - these are optional enhancements
|
|
605
618
|
if (this.settings.enableDetailedLogging) {
|
|
606
|
-
console.log(
|
|
619
|
+
console.log(
|
|
620
|
+
`[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}`
|
|
621
|
+
);
|
|
607
622
|
}
|
|
608
623
|
}
|
|
609
624
|
}
|
|
@@ -660,19 +675,21 @@ export class PortProxy {
|
|
|
660
675
|
// For keep-alive connections, just log a warning instead of closing
|
|
661
676
|
if (record.hasKeepAlive) {
|
|
662
677
|
console.log(
|
|
663
|
-
`[${connectionId}] Timeout event on incoming keep-alive connection from ${
|
|
678
|
+
`[${connectionId}] Timeout event on incoming keep-alive connection from ${
|
|
679
|
+
record.remoteIP
|
|
680
|
+
} after ${plugins.prettyMs(
|
|
664
681
|
this.settings.socketTimeout || 3600000
|
|
665
682
|
)}. Connection preserved.`
|
|
666
683
|
);
|
|
667
684
|
// Don't close the connection - just log
|
|
668
685
|
return;
|
|
669
686
|
}
|
|
670
|
-
|
|
687
|
+
|
|
671
688
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
672
689
|
console.log(
|
|
673
|
-
`[${connectionId}] Timeout on incoming side from ${
|
|
674
|
-
|
|
675
|
-
)}`
|
|
690
|
+
`[${connectionId}] Timeout on incoming side from ${
|
|
691
|
+
record.remoteIP
|
|
692
|
+
} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`
|
|
676
693
|
);
|
|
677
694
|
if (record.incomingTerminationReason === null) {
|
|
678
695
|
record.incomingTerminationReason = 'timeout';
|
|
@@ -685,19 +702,21 @@ export class PortProxy {
|
|
|
685
702
|
// For keep-alive connections, just log a warning instead of closing
|
|
686
703
|
if (record.hasKeepAlive) {
|
|
687
704
|
console.log(
|
|
688
|
-
`[${connectionId}] Timeout event on outgoing keep-alive connection from ${
|
|
705
|
+
`[${connectionId}] Timeout event on outgoing keep-alive connection from ${
|
|
706
|
+
record.remoteIP
|
|
707
|
+
} after ${plugins.prettyMs(
|
|
689
708
|
this.settings.socketTimeout || 3600000
|
|
690
709
|
)}. Connection preserved.`
|
|
691
710
|
);
|
|
692
711
|
// Don't close the connection - just log
|
|
693
712
|
return;
|
|
694
713
|
}
|
|
695
|
-
|
|
714
|
+
|
|
696
715
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
697
716
|
console.log(
|
|
698
|
-
`[${connectionId}] Timeout on outgoing side from ${
|
|
699
|
-
|
|
700
|
-
)}`
|
|
717
|
+
`[${connectionId}] Timeout on outgoing side from ${
|
|
718
|
+
record.remoteIP
|
|
719
|
+
} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`
|
|
701
720
|
);
|
|
702
721
|
if (record.outgoingTerminationReason === null) {
|
|
703
722
|
record.outgoingTerminationReason = 'timeout';
|
|
@@ -711,9 +730,11 @@ export class PortProxy {
|
|
|
711
730
|
// Disable timeouts completely for immortal connections
|
|
712
731
|
socket.setTimeout(0);
|
|
713
732
|
targetSocket.setTimeout(0);
|
|
714
|
-
|
|
733
|
+
|
|
715
734
|
if (this.settings.enableDetailedLogging) {
|
|
716
|
-
console.log(
|
|
735
|
+
console.log(
|
|
736
|
+
`[${connectionId}] Disabled socket timeouts for immortal keep-alive connection`
|
|
737
|
+
);
|
|
717
738
|
}
|
|
718
739
|
} else {
|
|
719
740
|
// Set normal timeouts for other connections
|
|
@@ -743,9 +764,7 @@ export class PortProxy {
|
|
|
743
764
|
const combinedData = Buffer.concat(record.pendingData);
|
|
744
765
|
targetSocket.write(combinedData, (err) => {
|
|
745
766
|
if (err) {
|
|
746
|
-
console.log(
|
|
747
|
-
`[${connectionId}] Error writing pending data to target: ${err.message}`
|
|
748
|
-
);
|
|
767
|
+
console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`);
|
|
749
768
|
return this.initiateCleanupOnce(record, 'write_error');
|
|
750
769
|
}
|
|
751
770
|
|
|
@@ -764,7 +783,9 @@ export class PortProxy {
|
|
|
764
783
|
? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
|
|
765
784
|
: ''
|
|
766
785
|
}` +
|
|
767
|
-
` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
786
|
+
` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
787
|
+
record.hasKeepAlive ? 'Yes' : 'No'
|
|
788
|
+
}`
|
|
768
789
|
);
|
|
769
790
|
} else {
|
|
770
791
|
console.log(
|
|
@@ -795,7 +816,9 @@ export class PortProxy {
|
|
|
795
816
|
? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
|
|
796
817
|
: ''
|
|
797
818
|
}` +
|
|
798
|
-
` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
819
|
+
` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
820
|
+
record.hasKeepAlive ? 'Yes' : 'No'
|
|
821
|
+
}`
|
|
799
822
|
);
|
|
800
823
|
} else {
|
|
801
824
|
console.log(
|
|
@@ -845,77 +868,91 @@ export class PortProxy {
|
|
|
845
868
|
if (record.cleanupTimer) {
|
|
846
869
|
clearTimeout(record.cleanupTimer);
|
|
847
870
|
}
|
|
848
|
-
|
|
871
|
+
|
|
849
872
|
// For immortal keep-alive connections, skip setting a timeout completely
|
|
850
873
|
if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal') {
|
|
851
874
|
if (this.settings.enableDetailedLogging) {
|
|
852
|
-
console.log(
|
|
875
|
+
console.log(
|
|
876
|
+
`[${connectionId}] Keep-alive connection with immortal treatment - no max lifetime`
|
|
877
|
+
);
|
|
853
878
|
}
|
|
854
879
|
// No cleanup timer for immortal connections
|
|
855
|
-
}
|
|
856
|
-
// For TLS keep-alive connections, use a
|
|
880
|
+
}
|
|
881
|
+
// For TLS keep-alive connections, use a moderately extended timeout
|
|
882
|
+
// but not too long to prevent certificate issues
|
|
857
883
|
else if (record.hasKeepAlive && record.isTLS) {
|
|
858
|
-
//
|
|
859
|
-
// This
|
|
860
|
-
const tlsKeepAliveTimeout =
|
|
884
|
+
// Use a shorter timeout for TLS connections to ensure certificate contexts are refreshed periodically
|
|
885
|
+
// This prevents issues with stale certificates in browser tabs that have been idle for a long time
|
|
886
|
+
const tlsKeepAliveTimeout = 8 * 60 * 60 * 1000; // 8 hours for TLS keep-alive - reduced from 14 days
|
|
861
887
|
const safeTimeout = ensureSafeTimeout(tlsKeepAliveTimeout);
|
|
862
|
-
|
|
888
|
+
|
|
863
889
|
record.cleanupTimer = setTimeout(() => {
|
|
864
890
|
console.log(
|
|
865
|
-
`[${connectionId}] TLS keep-alive connection from ${
|
|
891
|
+
`[${connectionId}] TLS keep-alive connection from ${
|
|
892
|
+
record.remoteIP
|
|
893
|
+
} exceeded max lifetime (${plugins.prettyMs(
|
|
866
894
|
tlsKeepAliveTimeout
|
|
867
|
-
)}), forcing cleanup.`
|
|
895
|
+
)}), forcing cleanup to refresh certificate context.`
|
|
868
896
|
);
|
|
869
|
-
this.initiateCleanupOnce(record, '
|
|
897
|
+
this.initiateCleanupOnce(record, 'tls_certificate_refresh');
|
|
870
898
|
}, safeTimeout);
|
|
871
|
-
|
|
899
|
+
|
|
872
900
|
// Make sure timeout doesn't keep the process alive
|
|
873
901
|
if (record.cleanupTimer.unref) {
|
|
874
902
|
record.cleanupTimer.unref();
|
|
875
903
|
}
|
|
876
|
-
|
|
904
|
+
|
|
877
905
|
if (this.settings.enableDetailedLogging) {
|
|
878
|
-
console.log(
|
|
906
|
+
console.log(
|
|
907
|
+
`[${connectionId}] TLS keep-alive connection with certificate refresh protection, lifetime: ${plugins.prettyMs(
|
|
908
|
+
tlsKeepAliveTimeout
|
|
909
|
+
)}`
|
|
910
|
+
);
|
|
879
911
|
}
|
|
880
912
|
}
|
|
881
913
|
// For extended keep-alive connections, use extended timeout
|
|
882
914
|
else if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'extended') {
|
|
883
915
|
const extendedTimeout = this.settings.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
884
916
|
const safeTimeout = ensureSafeTimeout(extendedTimeout);
|
|
885
|
-
|
|
917
|
+
|
|
886
918
|
record.cleanupTimer = setTimeout(() => {
|
|
887
919
|
console.log(
|
|
888
|
-
`[${connectionId}] Keep-alive connection from ${
|
|
889
|
-
|
|
890
|
-
)}), forcing cleanup.`
|
|
920
|
+
`[${connectionId}] Keep-alive connection from ${
|
|
921
|
+
record.remoteIP
|
|
922
|
+
} exceeded extended lifetime (${plugins.prettyMs(extendedTimeout)}), forcing cleanup.`
|
|
891
923
|
);
|
|
892
924
|
this.initiateCleanupOnce(record, 'extended_lifetime');
|
|
893
925
|
}, safeTimeout);
|
|
894
|
-
|
|
926
|
+
|
|
895
927
|
// Make sure timeout doesn't keep the process alive
|
|
896
928
|
if (record.cleanupTimer.unref) {
|
|
897
929
|
record.cleanupTimer.unref();
|
|
898
930
|
}
|
|
899
|
-
|
|
931
|
+
|
|
900
932
|
if (this.settings.enableDetailedLogging) {
|
|
901
|
-
console.log(
|
|
933
|
+
console.log(
|
|
934
|
+
`[${connectionId}] Keep-alive connection with extended lifetime of ${plugins.prettyMs(
|
|
935
|
+
extendedTimeout
|
|
936
|
+
)}`
|
|
937
|
+
);
|
|
902
938
|
}
|
|
903
939
|
}
|
|
904
940
|
// For standard connections, use normal timeout
|
|
905
941
|
else {
|
|
906
942
|
// Use domain-specific timeout if available, otherwise use default
|
|
907
|
-
const connectionTimeout =
|
|
943
|
+
const connectionTimeout =
|
|
944
|
+
record.domainConfig?.connectionTimeout || this.settings.maxConnectionLifetime!;
|
|
908
945
|
const safeTimeout = ensureSafeTimeout(connectionTimeout);
|
|
909
|
-
|
|
946
|
+
|
|
910
947
|
record.cleanupTimer = setTimeout(() => {
|
|
911
948
|
console.log(
|
|
912
|
-
`[${connectionId}] Connection from ${
|
|
913
|
-
|
|
914
|
-
)}), forcing cleanup.`
|
|
949
|
+
`[${connectionId}] Connection from ${
|
|
950
|
+
record.remoteIP
|
|
951
|
+
} exceeded max lifetime (${plugins.prettyMs(connectionTimeout)}), forcing cleanup.`
|
|
915
952
|
);
|
|
916
953
|
this.initiateCleanupOnce(record, 'connection_timeout');
|
|
917
954
|
}, safeTimeout);
|
|
918
|
-
|
|
955
|
+
|
|
919
956
|
// Make sure timeout doesn't keep the process alive
|
|
920
957
|
if (record.cleanupTimer.unref) {
|
|
921
958
|
record.cleanupTimer.unref();
|
|
@@ -999,35 +1036,46 @@ export class PortProxy {
|
|
|
999
1036
|
private updateActivity(record: IConnectionRecord): void {
|
|
1000
1037
|
// Get the current time
|
|
1001
1038
|
const now = Date.now();
|
|
1002
|
-
|
|
1039
|
+
|
|
1003
1040
|
// Check if there was a large time gap that suggests system sleep
|
|
1004
1041
|
if (record.lastActivity > 0) {
|
|
1005
1042
|
const timeDiff = now - record.lastActivity;
|
|
1006
|
-
|
|
1043
|
+
|
|
1007
1044
|
// If time difference is very large (> 30 minutes) and this is a keep-alive connection,
|
|
1008
1045
|
// this might indicate system sleep rather than just inactivity
|
|
1009
1046
|
if (timeDiff > 30 * 60 * 1000 && record.hasKeepAlive) {
|
|
1010
1047
|
if (this.settings.enableDetailedLogging) {
|
|
1011
1048
|
console.log(
|
|
1012
1049
|
`[${record.id}] Detected possible system sleep for ${plugins.prettyMs(timeDiff)}. ` +
|
|
1013
|
-
|
|
1050
|
+
`Handling keep-alive connection after long inactivity.`
|
|
1014
1051
|
);
|
|
1015
1052
|
}
|
|
1016
|
-
|
|
1017
|
-
// For keep-alive connections after sleep,
|
|
1053
|
+
|
|
1054
|
+
// For TLS keep-alive connections after sleep/long inactivity, force close
|
|
1055
|
+
// to make browser establish a new connection with fresh certificate context
|
|
1018
1056
|
if (record.isTLS && record.tlsHandshakeComplete) {
|
|
1019
|
-
|
|
1057
|
+
if (timeDiff > 4 * 60 * 60 * 1000) {
|
|
1058
|
+
// If inactive for more than 4 hours
|
|
1059
|
+
console.log(
|
|
1060
|
+
`[${record.id}] TLS connection inactive for ${plugins.prettyMs(timeDiff)}. ` +
|
|
1061
|
+
`Closing to force new connection with fresh certificate.`
|
|
1062
|
+
);
|
|
1063
|
+
return this.initiateCleanupOnce(record, 'certificate_refresh_needed');
|
|
1064
|
+
} else {
|
|
1065
|
+
// For shorter inactivity periods, try to refresh the TLS state
|
|
1066
|
+
this.refreshTlsStateAfterSleep(record);
|
|
1067
|
+
}
|
|
1020
1068
|
}
|
|
1021
|
-
|
|
1069
|
+
|
|
1022
1070
|
// Mark that we detected sleep
|
|
1023
1071
|
record.possibleSystemSleep = true;
|
|
1024
1072
|
record.lastSleepDetection = now;
|
|
1025
1073
|
}
|
|
1026
1074
|
}
|
|
1027
|
-
|
|
1075
|
+
|
|
1028
1076
|
// Update the activity timestamp
|
|
1029
1077
|
record.lastActivity = now;
|
|
1030
|
-
|
|
1078
|
+
|
|
1031
1079
|
// Clear any inactivity warning
|
|
1032
1080
|
if (record.inactivityWarningIssued) {
|
|
1033
1081
|
record.inactivityWarningIssued = false;
|
|
@@ -1042,22 +1090,37 @@ export class PortProxy {
|
|
|
1042
1090
|
if (record.usingNetworkProxy) {
|
|
1043
1091
|
return;
|
|
1044
1092
|
}
|
|
1045
|
-
|
|
1093
|
+
|
|
1046
1094
|
try {
|
|
1047
1095
|
// For outgoing connections that might need to be refreshed
|
|
1048
1096
|
if (record.outgoing && !record.outgoing.destroyed) {
|
|
1049
|
-
//
|
|
1097
|
+
// Check how long this connection has been established
|
|
1098
|
+
const connectionAge = Date.now() - record.incomingStartTime;
|
|
1099
|
+
const hourInMs = 60 * 60 * 1000;
|
|
1100
|
+
|
|
1101
|
+
// For TLS browser connections that are very old, it's better to force a new connection
|
|
1102
|
+
// rather than trying to refresh the state, to avoid certificate issues
|
|
1103
|
+
if (record.isTLS && record.hasKeepAlive && connectionAge > 12 * hourInMs) {
|
|
1104
|
+
console.log(
|
|
1105
|
+
`[${record.id}] Long-lived TLS connection (${plugins.prettyMs(connectionAge)}). ` +
|
|
1106
|
+
`Closing to ensure proper certificate handling on browser reconnect.`
|
|
1107
|
+
);
|
|
1108
|
+
return this.initiateCleanupOnce(record, 'certificate_context_refresh');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// For newer connections, try to send a refresh packet
|
|
1050
1112
|
record.outgoing.write(Buffer.alloc(0));
|
|
1051
|
-
|
|
1113
|
+
|
|
1052
1114
|
if (this.settings.enableDetailedLogging) {
|
|
1053
1115
|
console.log(`[${record.id}] Sent refresh packet after sleep detection`);
|
|
1054
1116
|
}
|
|
1055
1117
|
}
|
|
1056
1118
|
} catch (err) {
|
|
1057
1119
|
console.log(`[${record.id}] Error refreshing TLS state: ${err}`);
|
|
1058
|
-
|
|
1059
|
-
// If we
|
|
1060
|
-
//
|
|
1120
|
+
|
|
1121
|
+
// If we hit an error, it's likely the connection is already broken
|
|
1122
|
+
// Force cleanup to ensure browser reconnects cleanly
|
|
1123
|
+
return this.initiateCleanupOnce(record, 'tls_refresh_error');
|
|
1061
1124
|
}
|
|
1062
1125
|
}
|
|
1063
1126
|
|
|
@@ -1158,7 +1221,9 @@ export class PortProxy {
|
|
|
1158
1221
|
` Duration: ${plugins.prettyMs(
|
|
1159
1222
|
duration
|
|
1160
1223
|
)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` +
|
|
1161
|
-
`TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
1224
|
+
`TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${
|
|
1225
|
+
record.hasKeepAlive ? 'Yes' : 'No'
|
|
1226
|
+
}` +
|
|
1162
1227
|
`${record.usingNetworkProxy ? `, NetworkProxy: ${record.networkProxyIndex}` : ''}`
|
|
1163
1228
|
);
|
|
1164
1229
|
} else {
|
|
@@ -1181,7 +1246,7 @@ export class PortProxy {
|
|
|
1181
1246
|
}
|
|
1182
1247
|
return this.settings.targetIP!;
|
|
1183
1248
|
}
|
|
1184
|
-
|
|
1249
|
+
|
|
1185
1250
|
/**
|
|
1186
1251
|
* Initiates cleanup once for a connection
|
|
1187
1252
|
*/
|
|
@@ -1189,12 +1254,15 @@ export class PortProxy {
|
|
|
1189
1254
|
if (this.settings.enableDetailedLogging) {
|
|
1190
1255
|
console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`);
|
|
1191
1256
|
}
|
|
1192
|
-
|
|
1193
|
-
if (
|
|
1257
|
+
|
|
1258
|
+
if (
|
|
1259
|
+
record.incomingTerminationReason === null ||
|
|
1260
|
+
record.incomingTerminationReason === undefined
|
|
1261
|
+
) {
|
|
1194
1262
|
record.incomingTerminationReason = reason;
|
|
1195
1263
|
this.incrementTerminationStat('incoming', reason);
|
|
1196
1264
|
}
|
|
1197
|
-
|
|
1265
|
+
|
|
1198
1266
|
this.cleanupConnection(record, reason);
|
|
1199
1267
|
}
|
|
1200
1268
|
|
|
@@ -1318,7 +1386,7 @@ export class PortProxy {
|
|
|
1318
1386
|
|
|
1319
1387
|
// Apply socket optimizations
|
|
1320
1388
|
socket.setNoDelay(this.settings.noDelay);
|
|
1321
|
-
|
|
1389
|
+
|
|
1322
1390
|
// Create a unique connection ID and record
|
|
1323
1391
|
const connectionId = generateConnectionId();
|
|
1324
1392
|
const connectionRecord: IConnectionRecord = {
|
|
@@ -1342,19 +1410,19 @@ export class PortProxy {
|
|
|
1342
1410
|
hasKeepAlive: false, // Will set to true if keep-alive is applied
|
|
1343
1411
|
incomingTerminationReason: null,
|
|
1344
1412
|
outgoingTerminationReason: null,
|
|
1345
|
-
|
|
1413
|
+
|
|
1346
1414
|
// Initialize NetworkProxy tracking fields
|
|
1347
1415
|
usingNetworkProxy: false,
|
|
1348
|
-
|
|
1416
|
+
|
|
1349
1417
|
// Initialize sleep detection fields
|
|
1350
|
-
possibleSystemSleep: false
|
|
1418
|
+
possibleSystemSleep: false,
|
|
1351
1419
|
};
|
|
1352
|
-
|
|
1420
|
+
|
|
1353
1421
|
// Apply keep-alive settings if enabled
|
|
1354
1422
|
if (this.settings.keepAlive) {
|
|
1355
1423
|
socket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
1356
1424
|
connectionRecord.hasKeepAlive = true; // Mark connection as having keep-alive
|
|
1357
|
-
|
|
1425
|
+
|
|
1358
1426
|
// Apply enhanced TCP keep-alive options if enabled
|
|
1359
1427
|
if (this.settings.enableKeepAliveProbes) {
|
|
1360
1428
|
try {
|
|
@@ -1368,7 +1436,9 @@ export class PortProxy {
|
|
|
1368
1436
|
} catch (err) {
|
|
1369
1437
|
// Ignore errors - these are optional enhancements
|
|
1370
1438
|
if (this.settings.enableDetailedLogging) {
|
|
1371
|
-
console.log(
|
|
1439
|
+
console.log(
|
|
1440
|
+
`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`
|
|
1441
|
+
);
|
|
1372
1442
|
}
|
|
1373
1443
|
}
|
|
1374
1444
|
}
|
|
@@ -1381,8 +1451,8 @@ export class PortProxy {
|
|
|
1381
1451
|
if (this.settings.enableDetailedLogging) {
|
|
1382
1452
|
console.log(
|
|
1383
1453
|
`[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` +
|
|
1384
|
-
|
|
1385
|
-
|
|
1454
|
+
`Keep-Alive: ${connectionRecord.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
|
|
1455
|
+
`Active connections: ${this.connectionRecords.size}`
|
|
1386
1456
|
);
|
|
1387
1457
|
} else {
|
|
1388
1458
|
console.log(
|
|
@@ -1520,12 +1590,12 @@ export class PortProxy {
|
|
|
1520
1590
|
)}`
|
|
1521
1591
|
);
|
|
1522
1592
|
}
|
|
1523
|
-
|
|
1593
|
+
|
|
1524
1594
|
// Check if we should forward this to a NetworkProxy
|
|
1525
1595
|
if (
|
|
1526
|
-
isTlsHandshakeDetected &&
|
|
1527
|
-
domainConfig.useNetworkProxy === true &&
|
|
1528
|
-
initialChunk &&
|
|
1596
|
+
isTlsHandshakeDetected &&
|
|
1597
|
+
domainConfig.useNetworkProxy === true &&
|
|
1598
|
+
initialChunk &&
|
|
1529
1599
|
this.networkProxies.length > 0
|
|
1530
1600
|
) {
|
|
1531
1601
|
return this.forwardToNetworkProxy(
|
|
@@ -1763,11 +1833,11 @@ export class PortProxy {
|
|
|
1763
1833
|
} else {
|
|
1764
1834
|
nonTlsConnections++;
|
|
1765
1835
|
}
|
|
1766
|
-
|
|
1836
|
+
|
|
1767
1837
|
if (record.hasKeepAlive) {
|
|
1768
1838
|
keepAliveConnections++;
|
|
1769
1839
|
}
|
|
1770
|
-
|
|
1840
|
+
|
|
1771
1841
|
if (record.usingNetworkProxy) {
|
|
1772
1842
|
networkProxyConnections++;
|
|
1773
1843
|
}
|
|
@@ -1808,34 +1878,41 @@ export class PortProxy {
|
|
|
1808
1878
|
}
|
|
1809
1879
|
|
|
1810
1880
|
// Skip inactivity check if disabled or for immortal keep-alive connections
|
|
1811
|
-
if (
|
|
1812
|
-
|
|
1813
|
-
|
|
1881
|
+
if (
|
|
1882
|
+
!this.settings.disableInactivityCheck &&
|
|
1883
|
+
!(record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal')
|
|
1884
|
+
) {
|
|
1814
1885
|
const inactivityTime = now - record.lastActivity;
|
|
1815
|
-
|
|
1886
|
+
|
|
1816
1887
|
// Special handling for TLS keep-alive connections
|
|
1817
|
-
if (
|
|
1888
|
+
if (
|
|
1889
|
+
record.hasKeepAlive &&
|
|
1890
|
+
record.isTLS &&
|
|
1891
|
+
inactivityTime > this.settings.inactivityTimeout! / 2
|
|
1892
|
+
) {
|
|
1818
1893
|
// For TLS keep-alive connections that are getting stale, try to refresh before closing
|
|
1819
1894
|
if (!record.inactivityWarningIssued) {
|
|
1820
1895
|
console.log(
|
|
1821
|
-
`[${id}] TLS keep-alive connection from ${
|
|
1822
|
-
|
|
1896
|
+
`[${id}] TLS keep-alive connection from ${
|
|
1897
|
+
record.remoteIP
|
|
1898
|
+
} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
|
|
1899
|
+
`Attempting to preserve connection.`
|
|
1823
1900
|
);
|
|
1824
|
-
|
|
1901
|
+
|
|
1825
1902
|
// Set warning flag but give a much longer grace period for TLS connections
|
|
1826
1903
|
record.inactivityWarningIssued = true;
|
|
1827
|
-
|
|
1904
|
+
|
|
1828
1905
|
// For TLS connections, extend the last activity time considerably
|
|
1829
1906
|
// This gives browsers more time to re-establish the connection properly
|
|
1830
|
-
record.lastActivity = now -
|
|
1831
|
-
|
|
1907
|
+
record.lastActivity = now - this.settings.inactivityTimeout! / 3;
|
|
1908
|
+
|
|
1832
1909
|
// Try to stimulate the connection with a probe packet
|
|
1833
1910
|
if (record.outgoing && !record.outgoing.destroyed) {
|
|
1834
1911
|
try {
|
|
1835
1912
|
// For TLS connections, send a proper TLS heartbeat-like packet
|
|
1836
1913
|
// This is just a small empty buffer that won't affect the TLS session
|
|
1837
1914
|
record.outgoing.write(Buffer.alloc(0));
|
|
1838
|
-
|
|
1915
|
+
|
|
1839
1916
|
if (this.settings.enableDetailedLogging) {
|
|
1840
1917
|
console.log(`[${id}] Sent TLS keep-alive probe packet`);
|
|
1841
1918
|
}
|
|
@@ -1843,36 +1920,38 @@ export class PortProxy {
|
|
|
1843
1920
|
console.log(`[${id}] Error sending TLS probe packet: ${err}`);
|
|
1844
1921
|
}
|
|
1845
1922
|
}
|
|
1846
|
-
|
|
1923
|
+
|
|
1847
1924
|
// Don't proceed to the normal inactivity check logic
|
|
1848
1925
|
continue;
|
|
1849
1926
|
}
|
|
1850
1927
|
}
|
|
1851
|
-
|
|
1928
|
+
|
|
1852
1929
|
// Use extended timeout for extended-treatment keep-alive connections
|
|
1853
1930
|
let effectiveTimeout = this.settings.inactivityTimeout!;
|
|
1854
1931
|
if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'extended') {
|
|
1855
1932
|
const multiplier = this.settings.keepAliveInactivityMultiplier || 6;
|
|
1856
1933
|
effectiveTimeout = effectiveTimeout * multiplier;
|
|
1857
1934
|
}
|
|
1858
|
-
|
|
1935
|
+
|
|
1859
1936
|
if (inactivityTime > effectiveTimeout && !record.connectionClosed) {
|
|
1860
1937
|
// For keep-alive connections, issue a warning first
|
|
1861
1938
|
if (record.hasKeepAlive && !record.inactivityWarningIssued) {
|
|
1862
1939
|
console.log(
|
|
1863
|
-
`[${id}] Warning: Keep-alive connection from ${
|
|
1864
|
-
|
|
1940
|
+
`[${id}] Warning: Keep-alive connection from ${
|
|
1941
|
+
record.remoteIP
|
|
1942
|
+
} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
|
|
1943
|
+
`Will close in 10 minutes if no activity.`
|
|
1865
1944
|
);
|
|
1866
|
-
|
|
1945
|
+
|
|
1867
1946
|
// Set warning flag and add grace period
|
|
1868
1947
|
record.inactivityWarningIssued = true;
|
|
1869
1948
|
record.lastActivity = now - (effectiveTimeout - 600000);
|
|
1870
|
-
|
|
1949
|
+
|
|
1871
1950
|
// Try to stimulate activity with a probe packet
|
|
1872
1951
|
if (record.outgoing && !record.outgoing.destroyed) {
|
|
1873
1952
|
try {
|
|
1874
1953
|
record.outgoing.write(Buffer.alloc(0));
|
|
1875
|
-
|
|
1954
|
+
|
|
1876
1955
|
if (this.settings.enableDetailedLogging) {
|
|
1877
1956
|
console.log(`[${id}] Sent probe packet to test keep-alive connection`);
|
|
1878
1957
|
}
|
|
@@ -1882,22 +1961,37 @@ export class PortProxy {
|
|
|
1882
1961
|
}
|
|
1883
1962
|
} else {
|
|
1884
1963
|
// MODIFIED: For TLS connections, be more lenient before closing
|
|
1964
|
+
// For TLS browser connections, we need to handle certificate context properly
|
|
1885
1965
|
if (record.isTLS && record.hasKeepAlive) {
|
|
1886
|
-
// For
|
|
1887
|
-
//
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1966
|
+
// For very long inactivity, it's better to close the connection
|
|
1967
|
+
// so the browser establishes a new one with a fresh certificate context
|
|
1968
|
+
if (inactivityTime > 6 * 60 * 60 * 1000) {
|
|
1969
|
+
// 6 hours
|
|
1970
|
+
console.log(
|
|
1971
|
+
`[${id}] TLS keep-alive connection from ${
|
|
1972
|
+
record.remoteIP
|
|
1973
|
+
} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
|
|
1974
|
+
`Closing to ensure proper certificate handling on browser reconnect.`
|
|
1975
|
+
);
|
|
1976
|
+
this.cleanupConnection(record, 'tls_certificate_refresh');
|
|
1977
|
+
} else {
|
|
1978
|
+
// For shorter inactivity periods, add grace period
|
|
1979
|
+
console.log(
|
|
1980
|
+
`[${id}] TLS keep-alive connection from ${
|
|
1981
|
+
record.remoteIP
|
|
1982
|
+
} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
|
|
1983
|
+
`Adding extra grace period.`
|
|
1984
|
+
);
|
|
1985
|
+
|
|
1986
|
+
// Give additional time for browsers to reconnect properly
|
|
1987
|
+
record.lastActivity = now - effectiveTimeout / 2;
|
|
1988
|
+
}
|
|
1895
1989
|
} else {
|
|
1896
1990
|
// For non-keep-alive or after warning, close the connection
|
|
1897
1991
|
console.log(
|
|
1898
1992
|
`[${id}] Inactivity check: No activity on connection from ${record.remoteIP} ` +
|
|
1899
|
-
|
|
1900
|
-
|
|
1993
|
+
`for ${plugins.prettyMs(inactivityTime)}.` +
|
|
1994
|
+
(record.hasKeepAlive ? ' Despite keep-alive being enabled.' : '')
|
|
1901
1995
|
);
|
|
1902
1996
|
this.cleanupConnection(record, 'inactivity');
|
|
1903
1997
|
}
|
|
@@ -1905,7 +1999,9 @@ export class PortProxy {
|
|
|
1905
1999
|
} else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) {
|
|
1906
2000
|
// If activity detected after warning, clear the warning
|
|
1907
2001
|
if (this.settings.enableDetailedLogging) {
|
|
1908
|
-
console.log(
|
|
2002
|
+
console.log(
|
|
2003
|
+
`[${id}] Connection activity detected after inactivity warning, resetting warning`
|
|
2004
|
+
);
|
|
1909
2005
|
}
|
|
1910
2006
|
record.inactivityWarningIssued = false;
|
|
1911
2007
|
}
|
|
@@ -2054,4 +2150,4 @@ export class PortProxy {
|
|
|
2054
2150
|
|
|
2055
2151
|
console.log('PortProxy shutdown complete.');
|
|
2056
2152
|
}
|
|
2057
|
-
}
|
|
2153
|
+
}
|