@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.
@@ -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: settingsArg.enableKeepAliveProbes !== undefined
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 = domainConfig.networkProxyIndex !== undefined
421
- ? domainConfig.networkProxyIndex
422
- : 0;
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(`[${connectionId}] Invalid NetworkProxy index: ${proxyIndex}. Using fallback direct connection.`);
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(connectionId, socket, record, domainConfig, serverName, initialData);
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(`[${connectionId}] Client connection closed after forwarding to NetworkProxy`);
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) { // ContentType.handshake
502
+ if (chunk.length > 0 && chunk[0] === 22) {
503
+ // ContentType.handshake
493
504
  if (this.settings.enableDetailedLogging) {
494
- console.log(`[${connectionId}] Detected potential TLS handshake data while connected to NetworkProxy`);
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(`[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}`);
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 ${record.remoteIP} after ${plugins.prettyMs(
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 ${record.remoteIP} after ${plugins.prettyMs(
674
- this.settings.socketTimeout || 3600000
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 ${record.remoteIP} after ${plugins.prettyMs(
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 ${record.remoteIP} after ${plugins.prettyMs(
699
- this.settings.socketTimeout || 3600000
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(`[${connectionId}] Disabled socket timeouts for immortal keep-alive connection`);
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: ${record.hasKeepAlive ? 'Yes' : 'No'}`
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: ${record.hasKeepAlive ? 'Yes' : 'No'}`
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(`[${connectionId}] Keep-alive connection with immortal treatment - no max lifetime`);
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 very extended timeout
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
- // For TLS keep-alive connections, use a very extended timeout
859
- // This helps prevent certificate errors after sleep/wake cycles
860
- const tlsKeepAliveTimeout = 14 * 24 * 60 * 60 * 1000; // 14 days for TLS keep-alive
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 ${record.remoteIP} exceeded extended lifetime (${plugins.prettyMs(
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, 'tls_extended_lifetime');
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(`[${connectionId}] TLS keep-alive connection with enhanced protection, lifetime: ${plugins.prettyMs(tlsKeepAliveTimeout)}`);
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 ${record.remoteIP} exceeded extended lifetime (${plugins.prettyMs(
889
- extendedTimeout
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(`[${connectionId}] Keep-alive connection with extended lifetime of ${plugins.prettyMs(extendedTimeout)}`);
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 = record.domainConfig?.connectionTimeout || this.settings.maxConnectionLifetime!;
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 ${record.remoteIP} exceeded max lifetime (${plugins.prettyMs(
913
- connectionTimeout
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
- `Preserving keep-alive connection.`
1050
+ `Handling keep-alive connection after long inactivity.`
1014
1051
  );
1015
1052
  }
1016
-
1017
- // For keep-alive connections after sleep, we should refresh the TLS state if needed
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
- this.refreshTlsStateAfterSleep(record);
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
- // Send a zero-byte packet to test the connection
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 can't refresh, don't terminate - client will re-establish if needed
1060
- // Just log the issue but preserve the connection
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: ${record.hasKeepAlive ? 'Yes' : 'No'}` +
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 (record.incomingTerminationReason === null || record.incomingTerminationReason === undefined) {
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(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`);
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
- `Keep-Alive: ${connectionRecord.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
1385
- `Active connections: ${this.connectionRecords.size}`
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 (!this.settings.disableInactivityCheck &&
1812
- !(record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal')) {
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 (record.hasKeepAlive && record.isTLS && inactivityTime > this.settings.inactivityTimeout! / 2) {
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 ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
1822
- `Attempting to preserve connection.`
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 - (this.settings.inactivityTimeout! / 3);
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 ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
1864
- `Will close in 10 minutes if no activity.`
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 TLS keep-alive connections, add additional grace period
1887
- // This helps with browsers reconnecting after sleep
1888
- console.log(
1889
- `[${id}] TLS keep-alive connection from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. ` +
1890
- `Adding extra grace period.`
1891
- );
1892
-
1893
- // Give additional time for browsers to reconnect properly
1894
- record.lastActivity = now - (effectiveTimeout / 2);
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
- `for ${plugins.prettyMs(inactivityTime)}.` +
1900
- (record.hasKeepAlive ? ' Despite keep-alive being enabled.' : '')
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(`[${id}] Connection activity detected after inactivity warning, resetting warning`);
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
+ }