@push.rocks/smartproxy 3.41.5 → 3.41.7

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.
@@ -11,7 +11,7 @@ export interface IDomainConfig {
11
11
  portRanges?: Array<{ from: number; to: number }>; // Optional port ranges
12
12
  // Allow domain-specific timeout override
13
13
  connectionTimeout?: number; // Connection timeout override (ms)
14
-
14
+
15
15
  // NetworkProxy integration options for this specific domain
16
16
  useNetworkProxy?: boolean; // Whether to use NetworkProxy for this domain
17
17
  networkProxyPort?: number; // Override default NetworkProxy port for this domain
@@ -65,17 +65,17 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
65
65
  // NetworkProxy integration
66
66
  useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy
67
67
  networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
68
-
68
+
69
69
  // ACME certificate management options
70
70
  acme?: {
71
- enabled?: boolean; // Whether to enable automatic certificate management
72
- port?: number; // Port to listen on for ACME challenges (default: 80)
73
- contactEmail?: string; // Email for Let's Encrypt account
74
- useProduction?: boolean; // Whether to use Let's Encrypt production (default: false for staging)
75
- renewThresholdDays?: number; // Days before expiry to renew certificates (default: 30)
76
- autoRenew?: boolean; // Whether to automatically renew certificates (default: true)
77
- certificateStore?: string; // Directory to store certificates (default: ./certs)
78
- skipConfiguredCerts?: boolean; // Skip domains that already have certificates configured
71
+ enabled?: boolean; // Whether to enable automatic certificate management
72
+ port?: number; // Port to listen on for ACME challenges (default: 80)
73
+ contactEmail?: string; // Email for Let's Encrypt account
74
+ useProduction?: boolean; // Whether to use Let's Encrypt production (default: false for staging)
75
+ renewThresholdDays?: number; // Days before expiry to renew certificates (default: 30)
76
+ autoRenew?: boolean; // Whether to automatically renew certificates (default: true)
77
+ certificateStore?: string; // Directory to store certificates (default: ./certs)
78
+ skipConfiguredCerts?: boolean; // Skip domains that already have certificates configured
79
79
  };
80
80
  }
81
81
 
@@ -232,13 +232,13 @@ export class PortProxy {
232
232
 
233
233
  // Feature flags
234
234
  disableInactivityCheck: settingsArg.disableInactivityCheck || false,
235
- enableKeepAliveProbes: settingsArg.enableKeepAliveProbes !== undefined
236
- ? settingsArg.enableKeepAliveProbes : true,
235
+ enableKeepAliveProbes:
236
+ settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
237
237
  enableDetailedLogging: settingsArg.enableDetailedLogging || false,
238
238
  enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
239
239
  enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
240
- allowSessionTicket: settingsArg.allowSessionTicket !== undefined
241
- ? settingsArg.allowSessionTicket : true,
240
+ allowSessionTicket:
241
+ settingsArg.allowSessionTicket !== undefined ? settingsArg.allowSessionTicket : true,
242
242
 
243
243
  // Rate limiting defaults
244
244
  maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
@@ -248,10 +248,10 @@ export class PortProxy {
248
248
  keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
249
249
  keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
250
250
  extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000, // 7 days
251
-
251
+
252
252
  // NetworkProxy settings
253
253
  networkProxyPort: settingsArg.networkProxyPort || 8443, // Default NetworkProxy port
254
-
254
+
255
255
  // ACME certificate settings with reasonable defaults
256
256
  acme: settingsArg.acme || {
257
257
  enabled: false,
@@ -261,8 +261,8 @@ export class PortProxy {
261
261
  renewThresholdDays: 30,
262
262
  autoRenew: true,
263
263
  certificateStore: './certs',
264
- skipConfiguredCerts: false
265
- }
264
+ skipConfiguredCerts: false,
265
+ },
266
266
  };
267
267
 
268
268
  // Initialize NetworkProxy if enabled
@@ -280,23 +280,23 @@ export class PortProxy {
280
280
  const networkProxyOptions: any = {
281
281
  port: this.settings.networkProxyPort!,
282
282
  portProxyIntegration: true,
283
- logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
283
+ logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
284
284
  };
285
-
285
+
286
286
  // Add ACME settings if configured
287
287
  if (this.settings.acme) {
288
288
  networkProxyOptions.acme = { ...this.settings.acme };
289
289
  }
290
-
290
+
291
291
  this.networkProxy = new NetworkProxy(networkProxyOptions);
292
-
292
+
293
293
  console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
294
-
294
+
295
295
  // Convert and apply domain configurations to NetworkProxy
296
296
  await this.syncDomainConfigsToNetworkProxy();
297
297
  }
298
298
  }
299
-
299
+
300
300
  /**
301
301
  * Updates the domain configurations for the proxy
302
302
  * @param newDomainConfigs The new domain configurations
@@ -304,47 +304,47 @@ export class PortProxy {
304
304
  public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
305
305
  console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
306
306
  this.settings.domainConfigs = newDomainConfigs;
307
-
307
+
308
308
  // If NetworkProxy is initialized, resync the configurations
309
309
  if (this.networkProxy) {
310
310
  await this.syncDomainConfigsToNetworkProxy();
311
311
  }
312
312
  }
313
-
313
+
314
314
  /**
315
315
  * Updates the ACME certificate settings
316
316
  * @param acmeSettings New ACME settings
317
317
  */
318
318
  public async updateAcmeSettings(acmeSettings: IPortProxySettings['acme']): Promise<void> {
319
319
  console.log('Updating ACME certificate settings');
320
-
320
+
321
321
  // Update settings
322
322
  this.settings.acme = {
323
323
  ...this.settings.acme,
324
- ...acmeSettings
324
+ ...acmeSettings,
325
325
  };
326
-
326
+
327
327
  // If NetworkProxy is initialized, update its ACME settings
328
328
  if (this.networkProxy) {
329
329
  try {
330
330
  // Recreate NetworkProxy with new settings if ACME enabled state has changed
331
331
  if (this.settings.acme.enabled !== acmeSettings.enabled) {
332
332
  console.log(`ACME enabled state changed to: ${acmeSettings.enabled}`);
333
-
333
+
334
334
  // Stop the current NetworkProxy
335
335
  await this.networkProxy.stop();
336
336
  this.networkProxy = null;
337
-
337
+
338
338
  // Reinitialize with new settings
339
339
  await this.initializeNetworkProxy();
340
-
340
+
341
341
  // Use start() to make sure ACME gets initialized if newly enabled
342
342
  await this.networkProxy.start();
343
343
  } else {
344
344
  // Update existing NetworkProxy with new settings
345
345
  // Note: Some settings may require a restart to take effect
346
346
  console.log('Updating ACME settings in NetworkProxy');
347
-
347
+
348
348
  // For certificate renewals, we might want to trigger checks with the new settings
349
349
  if (acmeSettings.renewThresholdDays) {
350
350
  console.log(`Setting new renewal threshold to ${acmeSettings.renewThresholdDays} days`);
@@ -359,7 +359,7 @@ export class PortProxy {
359
359
  }
360
360
  }
361
361
  }
362
-
362
+
363
363
  /**
364
364
  * Synchronizes PortProxy domain configurations to NetworkProxy
365
365
  * This allows domains configured in PortProxy to be used by NetworkProxy
@@ -369,60 +369,67 @@ export class PortProxy {
369
369
  console.log('Cannot sync configurations - NetworkProxy not initialized');
370
370
  return;
371
371
  }
372
-
372
+
373
373
  try {
374
374
  // Get SSL certificates from assets
375
375
  // Import fs directly since it's not in plugins
376
376
  const fs = await import('fs');
377
-
377
+
378
378
  let certPair;
379
379
  try {
380
380
  certPair = {
381
381
  key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
382
- cert: fs.readFileSync('assets/certs/cert.pem', 'utf8')
382
+ cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'),
383
383
  };
384
384
  } catch (certError) {
385
385
  console.log(`Warning: Could not read default certificates: ${certError}`);
386
- console.log('Using empty certificate placeholders - ACME will generate proper certificates if enabled');
387
-
386
+ console.log(
387
+ 'Using empty certificate placeholders - ACME will generate proper certificates if enabled'
388
+ );
389
+
388
390
  // Use empty placeholders - NetworkProxy will use its internal defaults
389
391
  // or ACME will generate proper ones if enabled
390
392
  certPair = {
391
393
  key: '',
392
- cert: ''
394
+ cert: '',
393
395
  };
394
396
  }
395
-
397
+
396
398
  // Convert domain configs to NetworkProxy configs
397
399
  const proxyConfigs = this.networkProxy.convertPortProxyConfigs(
398
400
  this.settings.domainConfigs,
399
401
  certPair
400
402
  );
401
-
403
+
402
404
  // Log ACME-eligible domains if ACME is enabled
403
405
  if (this.settings.acme?.enabled) {
404
406
  const acmeEligibleDomains = proxyConfigs
405
- .filter(config => !config.hostName.includes('*')) // Exclude wildcards
406
- .map(config => config.hostName);
407
-
407
+ .filter((config) => !config.hostName.includes('*')) // Exclude wildcards
408
+ .map((config) => config.hostName);
409
+
408
410
  if (acmeEligibleDomains.length > 0) {
409
411
  console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
410
412
  } else {
411
413
  console.log('No domains eligible for ACME certificates found in configuration');
412
414
  }
413
415
  }
414
-
416
+
415
417
  // Update NetworkProxy with the converted configs
416
- this.networkProxy.updateProxyConfigs(proxyConfigs).then(() => {
417
- console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`);
418
- }).catch(err => {
419
- console.log(`Error synchronizing configurations: ${err.message}`);
420
- });
418
+ this.networkProxy
419
+ .updateProxyConfigs(proxyConfigs)
420
+ .then(() => {
421
+ console.log(
422
+ `Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`
423
+ );
424
+ })
425
+ .catch((err) => {
426
+ console.log(`Error synchronizing configurations: ${err.message}`);
427
+ });
421
428
  } catch (err) {
422
429
  console.log(`Failed to sync configurations: ${err}`);
423
430
  }
424
431
  }
425
-
432
+
426
433
  /**
427
434
  * Requests a certificate for a specific domain
428
435
  * @param domain The domain to request a certificate for
@@ -433,12 +440,12 @@ export class PortProxy {
433
440
  console.log('Cannot request certificate - NetworkProxy not initialized');
434
441
  return false;
435
442
  }
436
-
443
+
437
444
  if (!this.settings.acme?.enabled) {
438
445
  console.log('Cannot request certificate - ACME is not enabled');
439
446
  return false;
440
447
  }
441
-
448
+
442
449
  try {
443
450
  const result = await this.networkProxy.requestCertificate(domain);
444
451
  if (result) {
@@ -546,9 +553,7 @@ export class PortProxy {
546
553
  proxySocket.on('data', () => this.updateActivity(record));
547
554
 
548
555
  if (this.settings.enableDetailedLogging) {
549
- console.log(
550
- `[${connectionId}] TLS connection successfully forwarded to NetworkProxy`
551
- );
556
+ console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`);
552
557
  }
553
558
  });
554
559
  }
@@ -582,11 +587,11 @@ export class PortProxy {
582
587
  let queueSize = 0;
583
588
  let processingQueue = false;
584
589
  let drainPending = false;
585
-
590
+
586
591
  // Flag to track if we've switched to the final piping mechanism
587
592
  // Once this is true, we no longer buffer data in dataQueue
588
593
  let pipingEstablished = false;
589
-
594
+
590
595
  // Pause the incoming socket to prevent buffer overflows
591
596
  // This ensures we control the flow of data until piping is set up
592
597
  socket.pause();
@@ -594,22 +599,22 @@ export class PortProxy {
594
599
  // Function to safely process the data queue without losing events
595
600
  const processDataQueue = () => {
596
601
  if (processingQueue || dataQueue.length === 0 || pipingEstablished) return;
597
-
602
+
598
603
  processingQueue = true;
599
-
604
+
600
605
  try {
601
606
  // Process all queued chunks with the current active handler
602
607
  while (dataQueue.length > 0) {
603
608
  const chunk = dataQueue.shift()!;
604
609
  queueSize -= chunk.length;
605
-
610
+
606
611
  // Once piping is established, we shouldn't get here,
607
612
  // but just in case, pass to the outgoing socket directly
608
613
  if (pipingEstablished && record.outgoing) {
609
614
  record.outgoing.write(chunk);
610
615
  continue;
611
616
  }
612
-
617
+
613
618
  // Track bytes received
614
619
  record.bytesReceived += chunk.length;
615
620
 
@@ -643,7 +648,7 @@ export class PortProxy {
643
648
  }
644
649
  } finally {
645
650
  processingQueue = false;
646
-
651
+
647
652
  // If there's a pending drain and we've processed everything,
648
653
  // signal we're ready for more data if we haven't established piping yet
649
654
  if (drainPending && dataQueue.length === 0 && !pipingEstablished) {
@@ -657,17 +662,17 @@ export class PortProxy {
657
662
  const safeDataHandler = (chunk: Buffer) => {
658
663
  // If piping is already established, just let the pipe handle it
659
664
  if (pipingEstablished) return;
660
-
665
+
661
666
  // Add to our queue for orderly processing
662
667
  dataQueue.push(Buffer.from(chunk)); // Make a copy to be safe
663
668
  queueSize += chunk.length;
664
-
669
+
665
670
  // If queue is getting large, pause socket until we catch up
666
671
  if (this.settings.maxPendingDataSize && queueSize > this.settings.maxPendingDataSize * 0.8) {
667
672
  socket.pause();
668
673
  drainPending = true;
669
674
  }
670
-
675
+
671
676
  // Process the queue
672
677
  processDataQueue();
673
678
  };
@@ -848,19 +853,19 @@ export class PortProxy {
848
853
 
849
854
  // Process any remaining data in the queue before switching to piping
850
855
  processDataQueue();
851
-
856
+
852
857
  // Setup function to establish piping - we'll use this after flushing data
853
858
  const setupPiping = () => {
854
859
  // Mark that we're switching to piping mode
855
860
  pipingEstablished = true;
856
-
861
+
857
862
  // Setup piping in both directions
858
863
  socket.pipe(targetSocket);
859
864
  targetSocket.pipe(socket);
860
-
865
+
861
866
  // Resume the socket to ensure data flows
862
867
  socket.resume();
863
-
868
+
864
869
  // Process any data that might be queued in the interim
865
870
  if (dataQueue.length > 0) {
866
871
  // Write any remaining queued data directly to the target socket
@@ -871,7 +876,7 @@ export class PortProxy {
871
876
  dataQueue.length = 0;
872
877
  queueSize = 0;
873
878
  }
874
-
879
+
875
880
  if (this.settings.enableDetailedLogging) {
876
881
  console.log(
877
882
  `[${connectionId}] Connection established: ${record.remoteIP} -> ${targetHost}:${connectionOptions.port}` +
@@ -935,30 +940,36 @@ export class PortProxy {
935
940
  sourceIp: record.remoteIP,
936
941
  sourcePort: record.incoming.remotePort || 0,
937
942
  destIp: record.incoming.localAddress || '',
938
- destPort: record.incoming.localPort || 0
943
+ destPort: record.incoming.localPort || 0,
939
944
  };
940
-
945
+
941
946
  // Check for session tickets if allowSessionTicket is disabled
942
947
  if (this.settings.allowSessionTicket === false) {
943
948
  // Analyze for session resumption attempt (session ticket or PSK)
944
- const resumptionInfo = SniHandler.hasSessionResumption(renegChunk, this.settings.enableTlsDebugLogging);
945
-
949
+ const resumptionInfo = SniHandler.hasSessionResumption(
950
+ renegChunk,
951
+ this.settings.enableTlsDebugLogging
952
+ );
953
+
946
954
  if (resumptionInfo.isResumption) {
947
955
  // Always log resumption attempt for easier debugging
948
956
  // Try to extract SNI for logging
949
- const extractedSNI = SniHandler.extractSNI(renegChunk, this.settings.enableTlsDebugLogging);
957
+ const extractedSNI = SniHandler.extractSNI(
958
+ renegChunk,
959
+ this.settings.enableTlsDebugLogging
960
+ );
950
961
  console.log(
951
962
  `[${connectionId}] Session resumption detected in renegotiation. ` +
952
- `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
953
- `SNI value: ${extractedSNI || 'None'}, ` +
954
- `allowSessionTicket: ${this.settings.allowSessionTicket}`
963
+ `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
964
+ `SNI value: ${extractedSNI || 'None'}, ` +
965
+ `allowSessionTicket: ${this.settings.allowSessionTicket}`
955
966
  );
956
-
967
+
957
968
  // Block if there's session resumption without SNI
958
969
  if (!resumptionInfo.hasSNI) {
959
970
  console.log(
960
971
  `[${connectionId}] Session resumption detected in renegotiation without SNI and allowSessionTicket=false. ` +
961
- `Terminating connection to force new TLS handshake.`
972
+ `Terminating connection to force new TLS handshake.`
962
973
  );
963
974
  this.initiateCleanupOnce(record, 'session_ticket_blocked');
964
975
  return;
@@ -966,14 +977,18 @@ export class PortProxy {
966
977
  if (this.settings.enableDetailedLogging) {
967
978
  console.log(
968
979
  `[${connectionId}] Session resumption with SNI detected in renegotiation. ` +
969
- `Allowing connection since SNI is present.`
980
+ `Allowing connection since SNI is present.`
970
981
  );
971
982
  }
972
983
  }
973
984
  }
974
985
  }
975
-
976
- const newSNI = SniHandler.extractSNIWithResumptionSupport(renegChunk, connInfo, this.settings.enableTlsDebugLogging);
986
+
987
+ const newSNI = SniHandler.extractSNIWithResumptionSupport(
988
+ renegChunk,
989
+ connInfo,
990
+ this.settings.enableTlsDebugLogging
991
+ );
977
992
 
978
993
  // Skip if no SNI was found
979
994
  if (!newSNI) return;
@@ -983,7 +998,7 @@ export class PortProxy {
983
998
  // Log and terminate the connection for any SNI change
984
999
  console.log(
985
1000
  `[${connectionId}] Renegotiation with different SNI: ${record.lockedDomain} -> ${newSNI}. ` +
986
- `Terminating connection - SNI domain switching is not allowed.`
1001
+ `Terminating connection - SNI domain switching is not allowed.`
987
1002
  );
988
1003
  this.initiateCleanupOnce(record, 'sni_mismatch');
989
1004
  } else if (this.settings.enableDetailedLogging) {
@@ -1005,11 +1020,15 @@ export class PortProxy {
1005
1020
  // The renegotiation handler is added when piping is established
1006
1021
  // Making it part of setupPiping ensures proper sequencing of event handlers
1007
1022
  socket.on('data', renegotiationHandler);
1008
-
1023
+
1009
1024
  if (this.settings.enableDetailedLogging) {
1010
- console.log(`[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`);
1025
+ console.log(
1026
+ `[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`
1027
+ );
1011
1028
  if (this.settings.allowSessionTicket === false) {
1012
- console.log(`[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`);
1029
+ console.log(
1030
+ `[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`
1031
+ );
1013
1032
  }
1014
1033
  }
1015
1034
  }
@@ -1176,7 +1195,7 @@ export class PortProxy {
1176
1195
  try {
1177
1196
  // Remove our safe data handler
1178
1197
  record.incoming.removeAllListeners('data');
1179
-
1198
+
1180
1199
  // Reset the handler references
1181
1200
  record.renegotiationHandler = undefined;
1182
1201
  } catch (err) {
@@ -1402,7 +1421,11 @@ export class PortProxy {
1402
1421
  }
1403
1422
 
1404
1423
  // Initialize NetworkProxy if needed (useNetworkProxy is set but networkProxy isn't initialized)
1405
- if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0 && !this.networkProxy) {
1424
+ if (
1425
+ this.settings.useNetworkProxy &&
1426
+ this.settings.useNetworkProxy.length > 0 &&
1427
+ !this.networkProxy
1428
+ ) {
1406
1429
  await this.initializeNetworkProxy();
1407
1430
  }
1408
1431
 
@@ -1410,12 +1433,16 @@ export class PortProxy {
1410
1433
  if (this.networkProxy) {
1411
1434
  await this.networkProxy.start();
1412
1435
  console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);
1413
-
1436
+
1414
1437
  // Log ACME status
1415
1438
  if (this.settings.acme?.enabled) {
1416
- console.log(`ACME certificate management is enabled (${this.settings.acme.useProduction ? 'Production' : 'Staging'} mode)`);
1439
+ console.log(
1440
+ `ACME certificate management is enabled (${
1441
+ this.settings.acme.useProduction ? 'Production' : 'Staging'
1442
+ } mode)`
1443
+ );
1417
1444
  console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
1418
-
1445
+
1419
1446
  // Register domains for ACME certificates if enabled
1420
1447
  if (this.networkProxy.options.acme?.enabled) {
1421
1448
  console.log('Registering domains with ACME certificate manager...');
@@ -1489,7 +1516,7 @@ export class PortProxy {
1489
1516
 
1490
1517
  // Initialize browser connection tracking
1491
1518
  isBrowserConnection: false,
1492
- domainSwitches: 0,
1519
+ domainSwitches: 0,
1493
1520
  };
1494
1521
 
1495
1522
  // Apply keep-alive settings if enabled
@@ -1536,9 +1563,9 @@ export class PortProxy {
1536
1563
 
1537
1564
  // Check if this connection should be forwarded directly to NetworkProxy
1538
1565
  // First check port-based forwarding settings
1539
- let shouldUseNetworkProxy = this.settings.useNetworkProxy &&
1540
- this.settings.useNetworkProxy.includes(localPort);
1541
-
1566
+ let shouldUseNetworkProxy =
1567
+ this.settings.useNetworkProxy && this.settings.useNetworkProxy.includes(localPort);
1568
+
1542
1569
  // We'll look for domain-specific settings after SNI extraction
1543
1570
 
1544
1571
  if (shouldUseNetworkProxy) {
@@ -1577,13 +1604,13 @@ export class PortProxy {
1577
1604
 
1578
1605
  initialDataReceived = true;
1579
1606
  connectionRecord.hasReceivedInitialData = true;
1580
-
1607
+
1581
1608
  // Block non-TLS connections on port 443
1582
1609
  // Always enforce TLS on standard HTTPS port
1583
1610
  if (!SniHandler.isTlsHandshake(chunk) && localPort === 443) {
1584
1611
  console.log(
1585
1612
  `[${connectionId}] Non-TLS connection detected on port 443. ` +
1586
- `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
1613
+ `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
1587
1614
  );
1588
1615
  if (connectionRecord.incomingTerminationReason === null) {
1589
1616
  connectionRecord.incomingTerminationReason = 'non_tls_blocked';
@@ -1597,29 +1624,35 @@ export class PortProxy {
1597
1624
  // Check if this looks like a TLS handshake
1598
1625
  if (SniHandler.isTlsHandshake(chunk)) {
1599
1626
  connectionRecord.isTLS = true;
1600
-
1627
+
1601
1628
  // Check for TLS ClientHello with either no SNI or session tickets
1602
1629
  if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
1603
1630
  // Extract SNI first
1604
- const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
1631
+ const extractedSNI = SniHandler.extractSNI(
1632
+ chunk,
1633
+ this.settings.enableTlsDebugLogging
1634
+ );
1605
1635
  const hasSNI = !!extractedSNI;
1606
-
1636
+
1607
1637
  // Analyze for session resumption attempt
1608
- const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
1609
-
1638
+ const resumptionInfo = SniHandler.hasSessionResumption(
1639
+ chunk,
1640
+ this.settings.enableTlsDebugLogging
1641
+ );
1642
+
1610
1643
  // Always log for debugging purposes
1611
1644
  console.log(
1612
1645
  `[${connectionId}] TLS ClientHello detected with allowSessionTicket=false. ` +
1613
- `Has SNI: ${hasSNI ? 'Yes' : 'No'}, ` +
1614
- `SNI value: ${extractedSNI || 'None'}, ` +
1615
- `Has session resumption: ${resumptionInfo.isResumption ? 'Yes' : 'No'}`
1646
+ `Has SNI: ${hasSNI ? 'Yes' : 'No'}, ` +
1647
+ `SNI value: ${extractedSNI || 'None'}, ` +
1648
+ `Has session resumption: ${resumptionInfo.isResumption ? 'Yes' : 'No'}`
1616
1649
  );
1617
-
1650
+
1618
1651
  // Block if this is a connection with session resumption but no SNI
1619
1652
  if (resumptionInfo.isResumption && !hasSNI) {
1620
1653
  console.log(
1621
1654
  `[${connectionId}] Session resumption detected in initial ClientHello without SNI and allowSessionTicket=false. ` +
1622
- `Terminating connection to force new TLS handshake.`
1655
+ `Terminating connection to force new TLS handshake.`
1623
1656
  );
1624
1657
  if (connectionRecord.incomingTerminationReason === null) {
1625
1658
  connectionRecord.incomingTerminationReason = 'session_ticket_blocked';
@@ -1629,13 +1662,13 @@ export class PortProxy {
1629
1662
  this.cleanupConnection(connectionRecord, 'session_ticket_blocked');
1630
1663
  return;
1631
1664
  }
1632
-
1665
+
1633
1666
  // Also block if this is a TLS connection without SNI when allowSessionTicket is false
1634
1667
  // This forces clients to send SNI which helps with routing
1635
1668
  if (!hasSNI && localPort === 443) {
1636
1669
  console.log(
1637
1670
  `[${connectionId}] TLS ClientHello detected on port 443 without SNI and allowSessionTicket=false. ` +
1638
- `Terminating connection to force proper SNI in handshake.`
1671
+ `Terminating connection to force proper SNI in handshake.`
1639
1672
  );
1640
1673
  if (connectionRecord.incomingTerminationReason === null) {
1641
1674
  connectionRecord.incomingTerminationReason = 'no_sni_blocked';
@@ -1646,57 +1679,70 @@ export class PortProxy {
1646
1679
  return;
1647
1680
  }
1648
1681
  }
1649
-
1682
+
1650
1683
  // Try to extract SNI for domain-specific NetworkProxy handling
1651
1684
  const connInfo = {
1652
1685
  sourceIp: remoteIP,
1653
1686
  sourcePort: socket.remotePort || 0,
1654
1687
  destIp: socket.localAddress || '',
1655
- destPort: socket.localPort || 0
1688
+ destPort: socket.localPort || 0,
1656
1689
  };
1657
-
1690
+
1658
1691
  // Extract SNI to check for domain-specific NetworkProxy settings
1659
1692
  const serverName = SniHandler.processTlsPacket(
1660
- chunk,
1693
+ chunk,
1661
1694
  connInfo,
1662
1695
  this.settings.enableTlsDebugLogging
1663
1696
  );
1664
-
1697
+
1665
1698
  if (serverName) {
1666
1699
  // If we got an SNI, check for domain-specific NetworkProxy settings
1667
1700
  const domainConfig = this.settings.domainConfigs.find((config) =>
1668
1701
  config.domains.some((d) => plugins.minimatch(serverName, d))
1669
1702
  );
1670
-
1703
+
1671
1704
  // Save domain config and SNI in connection record
1672
1705
  connectionRecord.domainConfig = domainConfig;
1673
1706
  connectionRecord.lockedDomain = serverName;
1674
-
1707
+
1675
1708
  // Use domain-specific NetworkProxy port if configured
1676
1709
  if (domainConfig?.useNetworkProxy) {
1677
- const networkProxyPort = domainConfig.networkProxyPort || this.settings.networkProxyPort;
1678
-
1710
+ const networkProxyPort =
1711
+ domainConfig.networkProxyPort || this.settings.networkProxyPort;
1712
+
1679
1713
  if (this.settings.enableDetailedLogging) {
1680
1714
  console.log(
1681
1715
  `[${connectionId}] Using domain-specific NetworkProxy for ${serverName} on port ${networkProxyPort}`
1682
1716
  );
1683
1717
  }
1684
-
1718
+
1685
1719
  // Forward to NetworkProxy with domain-specific port
1686
- this.forwardToNetworkProxy(connectionId, socket, connectionRecord, chunk, networkProxyPort);
1720
+ this.forwardToNetworkProxy(
1721
+ connectionId,
1722
+ socket,
1723
+ connectionRecord,
1724
+ chunk,
1725
+ networkProxyPort
1726
+ );
1687
1727
  return;
1688
1728
  }
1689
1729
  }
1690
-
1730
+
1691
1731
  // Forward directly to NetworkProxy without domain-specific settings
1692
1732
  this.forwardToNetworkProxy(connectionId, socket, connectionRecord, chunk);
1693
1733
  } else {
1694
1734
  // If not TLS, use normal direct connection
1695
1735
  console.log(`[${connectionId}] Non-TLS connection on NetworkProxy port ${localPort}`);
1696
- this.setupDirectConnection(connectionId, socket, connectionRecord, undefined, undefined, chunk);
1736
+ this.setupDirectConnection(
1737
+ connectionId,
1738
+ socket,
1739
+ connectionRecord,
1740
+ undefined,
1741
+ undefined,
1742
+ chunk
1743
+ );
1697
1744
  }
1698
1745
  });
1699
-
1700
1746
  } else {
1701
1747
  // For non-NetworkProxy ports, proceed with normal processing
1702
1748
 
@@ -1760,9 +1806,9 @@ export class PortProxy {
1760
1806
  sourceIp: remoteIP,
1761
1807
  sourcePort: socket.remotePort || 0,
1762
1808
  destIp: socket.localAddress || '',
1763
- destPort: socket.localPort || 0
1809
+ destPort: socket.localPort || 0,
1764
1810
  };
1765
-
1811
+
1766
1812
  SniHandler.extractSNIWithResumptionSupport(chunk, debugConnInfo, true);
1767
1813
  }
1768
1814
  }
@@ -1814,7 +1860,7 @@ export class PortProxy {
1814
1860
 
1815
1861
  // Save domain config in connection record
1816
1862
  connectionRecord.domainConfig = domainConfig;
1817
-
1863
+
1818
1864
  // Check if this domain should use NetworkProxy (domain-specific setting)
1819
1865
  if (domainConfig?.useNetworkProxy && this.networkProxy) {
1820
1866
  if (this.settings.enableDetailedLogging) {
@@ -1822,15 +1868,16 @@ export class PortProxy {
1822
1868
  `[${connectionId}] Domain ${serverName} is configured to use NetworkProxy`
1823
1869
  );
1824
1870
  }
1825
-
1826
- const networkProxyPort = domainConfig.networkProxyPort || this.settings.networkProxyPort;
1827
-
1871
+
1872
+ const networkProxyPort =
1873
+ domainConfig.networkProxyPort || this.settings.networkProxyPort;
1874
+
1828
1875
  if (initialChunk && connectionRecord.isTLS) {
1829
1876
  // For TLS connections with initial chunk, forward to NetworkProxy
1830
1877
  this.forwardToNetworkProxy(
1831
- connectionId,
1832
- socket,
1833
- connectionRecord,
1878
+ connectionId,
1879
+ socket,
1880
+ connectionRecord,
1834
1881
  initialChunk,
1835
1882
  networkProxyPort // Pass the domain-specific NetworkProxy port if configured
1836
1883
  );
@@ -1861,7 +1908,10 @@ export class PortProxy {
1861
1908
  )}`
1862
1909
  );
1863
1910
  }
1864
- } else if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
1911
+ } else if (
1912
+ this.settings.defaultAllowedIPs &&
1913
+ this.settings.defaultAllowedIPs.length > 0
1914
+ ) {
1865
1915
  if (
1866
1916
  !isGlobIPAllowed(
1867
1917
  remoteIP,
@@ -1980,13 +2030,32 @@ export class PortProxy {
1980
2030
  }
1981
2031
 
1982
2032
  initialDataReceived = true;
1983
-
2033
+
2034
+ // ADD THE DEBUGGING CODE RIGHT HERE, BEFORE ANY OTHER PROCESSING
2035
+ if (SniHandler.isClientHello(chunk)) {
2036
+ // Log more details to understand session resumption
2037
+ const resumptionInfo = SniHandler.hasSessionResumption(chunk, true);
2038
+ console.log(
2039
+ `[${connectionId}] ClientHello details: isResumption=${resumptionInfo.isResumption}, hasSNI=${resumptionInfo.hasSNI}`
2040
+ );
2041
+
2042
+ // Try both extraction methods
2043
+ const standardSNI = SniHandler.extractSNI(chunk, true);
2044
+ const pskSNI = SniHandler.extractSNIFromPSKExtension(chunk, true);
2045
+
2046
+ console.log(
2047
+ `[${connectionId}] SNI extraction results: standardSNI=${
2048
+ standardSNI || 'none'
2049
+ }, pskSNI=${pskSNI || 'none'}`
2050
+ );
2051
+ }
2052
+
1984
2053
  // Block non-TLS connections on port 443
1985
2054
  // Always enforce TLS on standard HTTPS port
1986
2055
  if (!SniHandler.isTlsHandshake(chunk) && localPort === 443) {
1987
2056
  console.log(
1988
2057
  `[${connectionId}] Non-TLS connection detected on port 443 in SNI handler. ` +
1989
- `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
2058
+ `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`
1990
2059
  );
1991
2060
  if (connectionRecord.incomingTerminationReason === null) {
1992
2061
  connectionRecord.incomingTerminationReason = 'non_tls_blocked';
@@ -2008,28 +2077,34 @@ export class PortProxy {
2008
2077
  `[${connectionId}] Extracting SNI from TLS handshake, ${chunk.length} bytes`
2009
2078
  );
2010
2079
  }
2011
-
2080
+
2012
2081
  // Check for session tickets if allowSessionTicket is disabled
2013
2082
  if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
2014
2083
  // Analyze for session resumption attempt
2015
- const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
2016
-
2084
+ const resumptionInfo = SniHandler.hasSessionResumption(
2085
+ chunk,
2086
+ this.settings.enableTlsDebugLogging
2087
+ );
2088
+
2017
2089
  if (resumptionInfo.isResumption) {
2018
2090
  // Always log resumption attempt for easier debugging
2019
2091
  // Try to extract SNI for logging
2020
- const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
2092
+ const extractedSNI = SniHandler.extractSNI(
2093
+ chunk,
2094
+ this.settings.enableTlsDebugLogging
2095
+ );
2021
2096
  console.log(
2022
2097
  `[${connectionId}] Session resumption detected in SNI handler. ` +
2023
- `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
2024
- `SNI value: ${extractedSNI || 'None'}, ` +
2025
- `allowSessionTicket: ${this.settings.allowSessionTicket}`
2098
+ `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
2099
+ `SNI value: ${extractedSNI || 'None'}, ` +
2100
+ `allowSessionTicket: ${this.settings.allowSessionTicket}`
2026
2101
  );
2027
-
2102
+
2028
2103
  // Block if there's session resumption without SNI
2029
2104
  if (!resumptionInfo.hasSNI) {
2030
2105
  console.log(
2031
2106
  `[${connectionId}] Session resumption detected in SNI handler without SNI and allowSessionTicket=false. ` +
2032
- `Terminating connection to force new TLS handshake.`
2107
+ `Terminating connection to force new TLS handshake.`
2033
2108
  );
2034
2109
  if (connectionRecord.incomingTerminationReason === null) {
2035
2110
  connectionRecord.incomingTerminationReason = 'session_ticket_blocked';
@@ -2042,7 +2117,7 @@ export class PortProxy {
2042
2117
  if (this.settings.enableDetailedLogging) {
2043
2118
  console.log(
2044
2119
  `[${connectionId}] Session resumption with SNI detected in SNI handler. ` +
2045
- `Allowing connection since SNI is present.`
2120
+ `Allowing connection since SNI is present.`
2046
2121
  );
2047
2122
  }
2048
2123
  }
@@ -2054,16 +2129,17 @@ export class PortProxy {
2054
2129
  sourceIp: remoteIP,
2055
2130
  sourcePort: socket.remotePort || 0,
2056
2131
  destIp: socket.localAddress || '',
2057
- destPort: socket.localPort || 0
2132
+ destPort: socket.localPort || 0,
2058
2133
  };
2059
-
2134
+
2060
2135
  // Use the new processTlsPacket method for comprehensive handling
2061
- serverName = SniHandler.processTlsPacket(
2062
- chunk,
2063
- connInfo,
2064
- this.settings.enableTlsDebugLogging,
2065
- connectionRecord.lockedDomain // Pass any previously negotiated domain as a hint
2066
- ) || '';
2136
+ serverName =
2137
+ SniHandler.processTlsPacket(
2138
+ chunk,
2139
+ connInfo,
2140
+ this.settings.enableTlsDebugLogging,
2141
+ connectionRecord.lockedDomain // Pass any previously negotiated domain as a hint
2142
+ ) || '';
2067
2143
  }
2068
2144
 
2069
2145
  // Lock the connection to the negotiated SNI.
@@ -2392,7 +2468,7 @@ export class PortProxy {
2392
2468
  console.log('Stopping NetworkProxy...');
2393
2469
  await this.networkProxy.stop();
2394
2470
  console.log('NetworkProxy stopped successfully');
2395
-
2471
+
2396
2472
  // Log ACME shutdown if it was enabled
2397
2473
  if (this.settings.acme?.enabled) {
2398
2474
  console.log('ACME certificate manager stopped');
@@ -2417,4 +2493,4 @@ export class PortProxy {
2417
2493
 
2418
2494
  console.log('PortProxy shutdown complete.');
2419
2495
  }
2420
- }
2496
+ }