fix(core): Refactor PortProxy and SniHandler: improve configuration handling, logging, and whitespace consistency
This commit is contained in:
		@@ -1,5 +1,13 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-03-12 - 3.41.7 - fix(core)
 | 
				
			||||||
 | 
					Refactor PortProxy and SniHandler: improve configuration handling, logging, and whitespace consistency
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Standardized indentation and spacing for configuration properties in PortProxy settings (e.g. ACME options, keepAliveProbes, allowSessionTicket)
 | 
				
			||||||
 | 
					- Simplified conditional formatting and improved inline comments in PortProxy
 | 
				
			||||||
 | 
					- Enhanced logging messages in SniHandler for TLS handshake and session resumption detection
 | 
				
			||||||
 | 
					- Improved debugging output (e.g. hexdump of initial TLS packet) and consistency of multi-line expressions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-03-12 - 3.41.6 - fix(SniHandler)
 | 
					## 2025-03-12 - 3.41.6 - fix(SniHandler)
 | 
				
			||||||
Refactor SniHandler: update whitespace, comment formatting, and consistent type definitions
 | 
					Refactor SniHandler: update whitespace, comment formatting, and consistent type definitions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
export const commitinfo = {
 | 
					export const commitinfo = {
 | 
				
			||||||
  name: '@push.rocks/smartproxy',
 | 
					  name: '@push.rocks/smartproxy',
 | 
				
			||||||
  version: '3.41.6',
 | 
					  version: '3.41.7',
 | 
				
			||||||
  description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
 | 
					  description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -232,13 +232,13 @@ export class PortProxy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Feature flags
 | 
					      // Feature flags
 | 
				
			||||||
      disableInactivityCheck: settingsArg.disableInactivityCheck || false,
 | 
					      disableInactivityCheck: settingsArg.disableInactivityCheck || false,
 | 
				
			||||||
      enableKeepAliveProbes: settingsArg.enableKeepAliveProbes !== undefined 
 | 
					      enableKeepAliveProbes:
 | 
				
			||||||
                            ? settingsArg.enableKeepAliveProbes : true,
 | 
					        settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
 | 
				
			||||||
      enableDetailedLogging: settingsArg.enableDetailedLogging || false,
 | 
					      enableDetailedLogging: settingsArg.enableDetailedLogging || false,
 | 
				
			||||||
      enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
 | 
					      enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
 | 
				
			||||||
      enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
 | 
					      enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
 | 
				
			||||||
      allowSessionTicket: settingsArg.allowSessionTicket !== undefined 
 | 
					      allowSessionTicket:
 | 
				
			||||||
                         ? settingsArg.allowSessionTicket : true,
 | 
					        settingsArg.allowSessionTicket !== undefined ? settingsArg.allowSessionTicket : true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Rate limiting defaults
 | 
					      // Rate limiting defaults
 | 
				
			||||||
      maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
 | 
					      maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
 | 
				
			||||||
@@ -261,8 +261,8 @@ export class PortProxy {
 | 
				
			|||||||
        renewThresholdDays: 30,
 | 
					        renewThresholdDays: 30,
 | 
				
			||||||
        autoRenew: true,
 | 
					        autoRenew: true,
 | 
				
			||||||
        certificateStore: './certs',
 | 
					        certificateStore: './certs',
 | 
				
			||||||
        skipConfiguredCerts: false
 | 
					        skipConfiguredCerts: false,
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize NetworkProxy if enabled
 | 
					    // Initialize NetworkProxy if enabled
 | 
				
			||||||
@@ -280,7 +280,7 @@ export class PortProxy {
 | 
				
			|||||||
      const networkProxyOptions: any = {
 | 
					      const networkProxyOptions: any = {
 | 
				
			||||||
        port: this.settings.networkProxyPort!,
 | 
					        port: this.settings.networkProxyPort!,
 | 
				
			||||||
        portProxyIntegration: true,
 | 
					        portProxyIntegration: true,
 | 
				
			||||||
        logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
 | 
					        logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Add ACME settings if configured
 | 
					      // Add ACME settings if configured
 | 
				
			||||||
@@ -321,7 +321,7 @@ export class PortProxy {
 | 
				
			|||||||
    // Update settings
 | 
					    // Update settings
 | 
				
			||||||
    this.settings.acme = {
 | 
					    this.settings.acme = {
 | 
				
			||||||
      ...this.settings.acme,
 | 
					      ...this.settings.acme,
 | 
				
			||||||
      ...acmeSettings
 | 
					      ...acmeSettings,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // If NetworkProxy is initialized, update its ACME settings
 | 
					    // If NetworkProxy is initialized, update its ACME settings
 | 
				
			||||||
@@ -379,17 +379,19 @@ export class PortProxy {
 | 
				
			|||||||
      try {
 | 
					      try {
 | 
				
			||||||
        certPair = {
 | 
					        certPair = {
 | 
				
			||||||
          key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
 | 
					          key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
 | 
				
			||||||
          cert: fs.readFileSync('assets/certs/cert.pem', 'utf8')
 | 
					          cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      } catch (certError) {
 | 
					      } catch (certError) {
 | 
				
			||||||
        console.log(`Warning: Could not read default certificates: ${certError}`);
 | 
					        console.log(`Warning: Could not read default certificates: ${certError}`);
 | 
				
			||||||
        console.log('Using empty certificate placeholders - ACME will generate proper certificates if enabled');
 | 
					        console.log(
 | 
				
			||||||
 | 
					          'Using empty certificate placeholders - ACME will generate proper certificates if enabled'
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Use empty placeholders - NetworkProxy will use its internal defaults
 | 
					        // Use empty placeholders - NetworkProxy will use its internal defaults
 | 
				
			||||||
        // or ACME will generate proper ones if enabled
 | 
					        // or ACME will generate proper ones if enabled
 | 
				
			||||||
        certPair = {
 | 
					        certPair = {
 | 
				
			||||||
          key: '',
 | 
					          key: '',
 | 
				
			||||||
          cert: ''
 | 
					          cert: '',
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -402,8 +404,8 @@ export class PortProxy {
 | 
				
			|||||||
      // Log ACME-eligible domains if ACME is enabled
 | 
					      // Log ACME-eligible domains if ACME is enabled
 | 
				
			||||||
      if (this.settings.acme?.enabled) {
 | 
					      if (this.settings.acme?.enabled) {
 | 
				
			||||||
        const acmeEligibleDomains = proxyConfigs
 | 
					        const acmeEligibleDomains = proxyConfigs
 | 
				
			||||||
          .filter(config => !config.hostName.includes('*')) // Exclude wildcards
 | 
					          .filter((config) => !config.hostName.includes('*')) // Exclude wildcards
 | 
				
			||||||
          .map(config => config.hostName);
 | 
					          .map((config) => config.hostName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (acmeEligibleDomains.length > 0) {
 | 
					        if (acmeEligibleDomains.length > 0) {
 | 
				
			||||||
          console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
 | 
					          console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
 | 
				
			||||||
@@ -413,9 +415,14 @@ export class PortProxy {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Update NetworkProxy with the converted configs
 | 
					      // Update NetworkProxy with the converted configs
 | 
				
			||||||
      this.networkProxy.updateProxyConfigs(proxyConfigs).then(() => {
 | 
					      this.networkProxy
 | 
				
			||||||
        console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`);
 | 
					        .updateProxyConfigs(proxyConfigs)
 | 
				
			||||||
      }).catch(err => {
 | 
					        .then(() => {
 | 
				
			||||||
 | 
					          console.log(
 | 
				
			||||||
 | 
					            `Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((err) => {
 | 
				
			||||||
          console.log(`Error synchronizing configurations: ${err.message}`);
 | 
					          console.log(`Error synchronizing configurations: ${err.message}`);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
@@ -546,9 +553,7 @@ export class PortProxy {
 | 
				
			|||||||
      proxySocket.on('data', () => this.updateActivity(record));
 | 
					      proxySocket.on('data', () => this.updateActivity(record));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.settings.enableDetailedLogging) {
 | 
					      if (this.settings.enableDetailedLogging) {
 | 
				
			||||||
        console.log(
 | 
					        console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`);
 | 
				
			||||||
          `[${connectionId}] TLS connection successfully forwarded to NetworkProxy`
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -935,18 +940,24 @@ export class PortProxy {
 | 
				
			|||||||
                sourceIp: record.remoteIP,
 | 
					                sourceIp: record.remoteIP,
 | 
				
			||||||
                sourcePort: record.incoming.remotePort || 0,
 | 
					                sourcePort: record.incoming.remotePort || 0,
 | 
				
			||||||
                destIp: record.incoming.localAddress || '',
 | 
					                destIp: record.incoming.localAddress || '',
 | 
				
			||||||
                destPort: record.incoming.localPort || 0
 | 
					                destPort: record.incoming.localPort || 0,
 | 
				
			||||||
              };
 | 
					              };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // Check for session tickets if allowSessionTicket is disabled
 | 
					              // Check for session tickets if allowSessionTicket is disabled
 | 
				
			||||||
              if (this.settings.allowSessionTicket === false) {
 | 
					              if (this.settings.allowSessionTicket === false) {
 | 
				
			||||||
                // Analyze for session resumption attempt (session ticket or PSK)
 | 
					                // Analyze for session resumption attempt (session ticket or PSK)
 | 
				
			||||||
                const resumptionInfo = SniHandler.hasSessionResumption(renegChunk, this.settings.enableTlsDebugLogging);
 | 
					                const resumptionInfo = SniHandler.hasSessionResumption(
 | 
				
			||||||
 | 
					                  renegChunk,
 | 
				
			||||||
 | 
					                  this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (resumptionInfo.isResumption) {
 | 
					                if (resumptionInfo.isResumption) {
 | 
				
			||||||
                  // Always log resumption attempt for easier debugging
 | 
					                  // Always log resumption attempt for easier debugging
 | 
				
			||||||
                  // Try to extract SNI for logging
 | 
					                  // Try to extract SNI for logging
 | 
				
			||||||
                  const extractedSNI = SniHandler.extractSNI(renegChunk, this.settings.enableTlsDebugLogging);
 | 
					                  const extractedSNI = SniHandler.extractSNI(
 | 
				
			||||||
 | 
					                    renegChunk,
 | 
				
			||||||
 | 
					                    this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
                  console.log(
 | 
					                  console.log(
 | 
				
			||||||
                    `[${connectionId}] Session resumption detected in renegotiation. ` +
 | 
					                    `[${connectionId}] Session resumption detected in renegotiation. ` +
 | 
				
			||||||
                      `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
 | 
					                      `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
 | 
				
			||||||
@@ -973,7 +984,11 @@ export class PortProxy {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              const newSNI = SniHandler.extractSNIWithResumptionSupport(renegChunk, connInfo, this.settings.enableTlsDebugLogging);
 | 
					              const newSNI = SniHandler.extractSNIWithResumptionSupport(
 | 
				
			||||||
 | 
					                renegChunk,
 | 
				
			||||||
 | 
					                connInfo,
 | 
				
			||||||
 | 
					                this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // Skip if no SNI was found
 | 
					              // Skip if no SNI was found
 | 
				
			||||||
              if (!newSNI) return;
 | 
					              if (!newSNI) return;
 | 
				
			||||||
@@ -1007,9 +1022,13 @@ export class PortProxy {
 | 
				
			|||||||
        socket.on('data', renegotiationHandler);
 | 
					        socket.on('data', renegotiationHandler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.settings.enableDetailedLogging) {
 | 
					        if (this.settings.enableDetailedLogging) {
 | 
				
			||||||
          console.log(`[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`);
 | 
					          console.log(
 | 
				
			||||||
 | 
					            `[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
          if (this.settings.allowSessionTicket === false) {
 | 
					          if (this.settings.allowSessionTicket === false) {
 | 
				
			||||||
            console.log(`[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`);
 | 
					            console.log(
 | 
				
			||||||
 | 
					              `[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -1402,7 +1421,11 @@ export class PortProxy {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize NetworkProxy if needed (useNetworkProxy is set but networkProxy isn't initialized)
 | 
					    // Initialize NetworkProxy if needed (useNetworkProxy is set but networkProxy isn't initialized)
 | 
				
			||||||
    if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0 && !this.networkProxy) {
 | 
					    if (
 | 
				
			||||||
 | 
					      this.settings.useNetworkProxy &&
 | 
				
			||||||
 | 
					      this.settings.useNetworkProxy.length > 0 &&
 | 
				
			||||||
 | 
					      !this.networkProxy
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      await this.initializeNetworkProxy();
 | 
					      await this.initializeNetworkProxy();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1413,7 +1436,11 @@ export class PortProxy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Log ACME status
 | 
					      // Log ACME status
 | 
				
			||||||
      if (this.settings.acme?.enabled) {
 | 
					      if (this.settings.acme?.enabled) {
 | 
				
			||||||
        console.log(`ACME certificate management is enabled (${this.settings.acme.useProduction ? 'Production' : 'Staging'} mode)`);
 | 
					        console.log(
 | 
				
			||||||
 | 
					          `ACME certificate management is enabled (${
 | 
				
			||||||
 | 
					            this.settings.acme.useProduction ? 'Production' : 'Staging'
 | 
				
			||||||
 | 
					          } mode)`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
 | 
					        console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Register domains for ACME certificates if enabled
 | 
					        // Register domains for ACME certificates if enabled
 | 
				
			||||||
@@ -1536,8 +1563,8 @@ export class PortProxy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Check if this connection should be forwarded directly to NetworkProxy
 | 
					      // Check if this connection should be forwarded directly to NetworkProxy
 | 
				
			||||||
      // First check port-based forwarding settings
 | 
					      // First check port-based forwarding settings
 | 
				
			||||||
      let shouldUseNetworkProxy = this.settings.useNetworkProxy && 
 | 
					      let shouldUseNetworkProxy =
 | 
				
			||||||
                                 this.settings.useNetworkProxy.includes(localPort);
 | 
					        this.settings.useNetworkProxy && this.settings.useNetworkProxy.includes(localPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // We'll look for domain-specific settings after SNI extraction
 | 
					      // We'll look for domain-specific settings after SNI extraction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1601,11 +1628,17 @@ export class PortProxy {
 | 
				
			|||||||
            // Check for TLS ClientHello with either no SNI or session tickets
 | 
					            // Check for TLS ClientHello with either no SNI or session tickets
 | 
				
			||||||
            if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
 | 
					            if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
 | 
				
			||||||
              // Extract SNI first
 | 
					              // Extract SNI first
 | 
				
			||||||
              const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
 | 
					              const extractedSNI = SniHandler.extractSNI(
 | 
				
			||||||
 | 
					                chunk,
 | 
				
			||||||
 | 
					                this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
              const hasSNI = !!extractedSNI;
 | 
					              const hasSNI = !!extractedSNI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // Analyze for session resumption attempt
 | 
					              // Analyze for session resumption attempt
 | 
				
			||||||
              const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
 | 
					              const resumptionInfo = SniHandler.hasSessionResumption(
 | 
				
			||||||
 | 
					                chunk,
 | 
				
			||||||
 | 
					                this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // Always log for debugging purposes
 | 
					              // Always log for debugging purposes
 | 
				
			||||||
              console.log(
 | 
					              console.log(
 | 
				
			||||||
@@ -1652,7 +1685,7 @@ export class PortProxy {
 | 
				
			|||||||
              sourceIp: remoteIP,
 | 
					              sourceIp: remoteIP,
 | 
				
			||||||
              sourcePort: socket.remotePort || 0,
 | 
					              sourcePort: socket.remotePort || 0,
 | 
				
			||||||
              destIp: socket.localAddress || '',
 | 
					              destIp: socket.localAddress || '',
 | 
				
			||||||
              destPort: socket.localPort || 0
 | 
					              destPort: socket.localPort || 0,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Extract SNI to check for domain-specific NetworkProxy settings
 | 
					            // Extract SNI to check for domain-specific NetworkProxy settings
 | 
				
			||||||
@@ -1674,7 +1707,8 @@ export class PortProxy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
              // Use domain-specific NetworkProxy port if configured
 | 
					              // Use domain-specific NetworkProxy port if configured
 | 
				
			||||||
              if (domainConfig?.useNetworkProxy) {
 | 
					              if (domainConfig?.useNetworkProxy) {
 | 
				
			||||||
                const networkProxyPort = domainConfig.networkProxyPort || this.settings.networkProxyPort;
 | 
					                const networkProxyPort =
 | 
				
			||||||
 | 
					                  domainConfig.networkProxyPort || this.settings.networkProxyPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (this.settings.enableDetailedLogging) {
 | 
					                if (this.settings.enableDetailedLogging) {
 | 
				
			||||||
                  console.log(
 | 
					                  console.log(
 | 
				
			||||||
@@ -1683,7 +1717,13 @@ export class PortProxy {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Forward to NetworkProxy with domain-specific port
 | 
					                // Forward to NetworkProxy with domain-specific port
 | 
				
			||||||
                this.forwardToNetworkProxy(connectionId, socket, connectionRecord, chunk, networkProxyPort);
 | 
					                this.forwardToNetworkProxy(
 | 
				
			||||||
 | 
					                  connectionId,
 | 
				
			||||||
 | 
					                  socket,
 | 
				
			||||||
 | 
					                  connectionRecord,
 | 
				
			||||||
 | 
					                  chunk,
 | 
				
			||||||
 | 
					                  networkProxyPort
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -1693,10 +1733,16 @@ export class PortProxy {
 | 
				
			|||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            // If not TLS, use normal direct connection
 | 
					            // If not TLS, use normal direct connection
 | 
				
			||||||
            console.log(`[${connectionId}] Non-TLS connection on NetworkProxy port ${localPort}`);
 | 
					            console.log(`[${connectionId}] Non-TLS connection on NetworkProxy port ${localPort}`);
 | 
				
			||||||
            this.setupDirectConnection(connectionId, socket, connectionRecord, undefined, undefined, chunk);
 | 
					            this.setupDirectConnection(
 | 
				
			||||||
 | 
					              connectionId,
 | 
				
			||||||
 | 
					              socket,
 | 
				
			||||||
 | 
					              connectionRecord,
 | 
				
			||||||
 | 
					              undefined,
 | 
				
			||||||
 | 
					              undefined,
 | 
				
			||||||
 | 
					              chunk
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // For non-NetworkProxy ports, proceed with normal processing
 | 
					        // For non-NetworkProxy ports, proceed with normal processing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1760,7 +1806,7 @@ export class PortProxy {
 | 
				
			|||||||
                sourceIp: remoteIP,
 | 
					                sourceIp: remoteIP,
 | 
				
			||||||
                sourcePort: socket.remotePort || 0,
 | 
					                sourcePort: socket.remotePort || 0,
 | 
				
			||||||
                destIp: socket.localAddress || '',
 | 
					                destIp: socket.localAddress || '',
 | 
				
			||||||
                destPort: socket.localPort || 0
 | 
					                destPort: socket.localPort || 0,
 | 
				
			||||||
              };
 | 
					              };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              SniHandler.extractSNIWithResumptionSupport(chunk, debugConnInfo, true);
 | 
					              SniHandler.extractSNIWithResumptionSupport(chunk, debugConnInfo, true);
 | 
				
			||||||
@@ -1823,7 +1869,8 @@ export class PortProxy {
 | 
				
			|||||||
              );
 | 
					              );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const networkProxyPort = domainConfig.networkProxyPort || this.settings.networkProxyPort;
 | 
					            const networkProxyPort =
 | 
				
			||||||
 | 
					              domainConfig.networkProxyPort || this.settings.networkProxyPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (initialChunk && connectionRecord.isTLS) {
 | 
					            if (initialChunk && connectionRecord.isTLS) {
 | 
				
			||||||
              // For TLS connections with initial chunk, forward to NetworkProxy
 | 
					              // For TLS connections with initial chunk, forward to NetworkProxy
 | 
				
			||||||
@@ -1861,7 +1908,10 @@ export class PortProxy {
 | 
				
			|||||||
                )}`
 | 
					                )}`
 | 
				
			||||||
              );
 | 
					              );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          } else if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
 | 
					          } else if (
 | 
				
			||||||
 | 
					            this.settings.defaultAllowedIPs &&
 | 
				
			||||||
 | 
					            this.settings.defaultAllowedIPs.length > 0
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
            if (
 | 
					            if (
 | 
				
			||||||
              !isGlobIPAllowed(
 | 
					              !isGlobIPAllowed(
 | 
				
			||||||
                remoteIP,
 | 
					                remoteIP,
 | 
				
			||||||
@@ -1981,6 +2031,25 @@ export class PortProxy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            initialDataReceived = true;
 | 
					            initialDataReceived = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // ADD THE DEBUGGING CODE RIGHT HERE, BEFORE ANY OTHER PROCESSING
 | 
				
			||||||
 | 
					            if (SniHandler.isClientHello(chunk)) {
 | 
				
			||||||
 | 
					              // Log more details to understand session resumption
 | 
				
			||||||
 | 
					              const resumptionInfo = SniHandler.hasSessionResumption(chunk, true);
 | 
				
			||||||
 | 
					              console.log(
 | 
				
			||||||
 | 
					                `[${connectionId}] ClientHello details: isResumption=${resumptionInfo.isResumption}, hasSNI=${resumptionInfo.hasSNI}`
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Try both extraction methods
 | 
				
			||||||
 | 
					              const standardSNI = SniHandler.extractSNI(chunk, true);
 | 
				
			||||||
 | 
					              const pskSNI = SniHandler.extractSNIFromPSKExtension(chunk, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              console.log(
 | 
				
			||||||
 | 
					                `[${connectionId}] SNI extraction results: standardSNI=${
 | 
				
			||||||
 | 
					                  standardSNI || 'none'
 | 
				
			||||||
 | 
					                }, pskSNI=${pskSNI || 'none'}`
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Block non-TLS connections on port 443
 | 
					            // Block non-TLS connections on port 443
 | 
				
			||||||
            // Always enforce TLS on standard HTTPS port
 | 
					            // Always enforce TLS on standard HTTPS port
 | 
				
			||||||
            if (!SniHandler.isTlsHandshake(chunk) && localPort === 443) {
 | 
					            if (!SniHandler.isTlsHandshake(chunk) && localPort === 443) {
 | 
				
			||||||
@@ -2012,12 +2081,18 @@ export class PortProxy {
 | 
				
			|||||||
              // Check for session tickets if allowSessionTicket is disabled
 | 
					              // Check for session tickets if allowSessionTicket is disabled
 | 
				
			||||||
              if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
 | 
					              if (this.settings.allowSessionTicket === false && SniHandler.isClientHello(chunk)) {
 | 
				
			||||||
                // Analyze for session resumption attempt
 | 
					                // Analyze for session resumption attempt
 | 
				
			||||||
                const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
 | 
					                const resumptionInfo = SniHandler.hasSessionResumption(
 | 
				
			||||||
 | 
					                  chunk,
 | 
				
			||||||
 | 
					                  this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (resumptionInfo.isResumption) {
 | 
					                if (resumptionInfo.isResumption) {
 | 
				
			||||||
                  // Always log resumption attempt for easier debugging
 | 
					                  // Always log resumption attempt for easier debugging
 | 
				
			||||||
                  // Try to extract SNI for logging
 | 
					                  // Try to extract SNI for logging
 | 
				
			||||||
                  const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
 | 
					                  const extractedSNI = SniHandler.extractSNI(
 | 
				
			||||||
 | 
					                    chunk,
 | 
				
			||||||
 | 
					                    this.settings.enableTlsDebugLogging
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
                  console.log(
 | 
					                  console.log(
 | 
				
			||||||
                    `[${connectionId}] Session resumption detected in SNI handler. ` +
 | 
					                    `[${connectionId}] Session resumption detected in SNI handler. ` +
 | 
				
			||||||
                      `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
 | 
					                      `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
 | 
				
			||||||
@@ -2054,11 +2129,12 @@ export class PortProxy {
 | 
				
			|||||||
                sourceIp: remoteIP,
 | 
					                sourceIp: remoteIP,
 | 
				
			||||||
                sourcePort: socket.remotePort || 0,
 | 
					                sourcePort: socket.remotePort || 0,
 | 
				
			||||||
                destIp: socket.localAddress || '',
 | 
					                destIp: socket.localAddress || '',
 | 
				
			||||||
                destPort: socket.localPort || 0
 | 
					                destPort: socket.localPort || 0,
 | 
				
			||||||
              };
 | 
					              };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // Use the new processTlsPacket method for comprehensive handling
 | 
					              // Use the new processTlsPacket method for comprehensive handling
 | 
				
			||||||
              serverName = SniHandler.processTlsPacket(
 | 
					              serverName =
 | 
				
			||||||
 | 
					                SniHandler.processTlsPacket(
 | 
				
			||||||
                  chunk,
 | 
					                  chunk,
 | 
				
			||||||
                  connInfo,
 | 
					                  connInfo,
 | 
				
			||||||
                  this.settings.enableTlsDebugLogging,
 | 
					                  this.settings.enableTlsDebugLogging,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1321,6 +1321,7 @@ export class SniHandler {
 | 
				
			|||||||
   * @param cachedSni - Optional cached SNI from previous connections (for racing detection)
 | 
					   * @param cachedSni - Optional cached SNI from previous connections (for racing detection)
 | 
				
			||||||
   * @returns The extracted server name or undefined if not found or more data needed
 | 
					   * @returns The extracted server name or undefined if not found or more data needed
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static processTlsPacket(
 | 
					  public static processTlsPacket(
 | 
				
			||||||
    buffer: Buffer,
 | 
					    buffer: Buffer,
 | 
				
			||||||
    connectionInfo: {
 | 
					    connectionInfo: {
 | 
				
			||||||
@@ -1373,6 +1374,74 @@ export class SniHandler {
 | 
				
			|||||||
      return undefined;
 | 
					      return undefined;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Enhanced session resumption detection
 | 
				
			||||||
 | 
					    if (this.isClientHello(buffer)) {
 | 
				
			||||||
 | 
					      const resumptionInfo = this.hasSessionResumption(buffer, enableLogging);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (resumptionInfo.isResumption) {
 | 
				
			||||||
 | 
					        log(`Session resumption detected in TLS packet`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Always try standard SNI extraction first
 | 
				
			||||||
 | 
					        const standardSni = this.extractSNI(buffer, enableLogging);
 | 
				
			||||||
 | 
					        if (standardSni) {
 | 
				
			||||||
 | 
					          log(`Found standard SNI in session resumption: ${standardSni}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Cache this SNI
 | 
				
			||||||
 | 
					          this.cacheSession(connectionInfo.sourceIp, standardSni);
 | 
				
			||||||
 | 
					          return standardSni;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Enhanced session resumption SNI extraction
 | 
				
			||||||
 | 
					        // Try extracting from PSK identity
 | 
				
			||||||
 | 
					        const pskSni = this.extractSNIFromPSKExtension(buffer, enableLogging);
 | 
				
			||||||
 | 
					        if (pskSni) {
 | 
				
			||||||
 | 
					          log(`Extracted SNI from PSK extension: ${pskSni}`);
 | 
				
			||||||
 | 
					          this.cacheSession(connectionInfo.sourceIp, pskSni);
 | 
				
			||||||
 | 
					          return pskSni;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Additional check for SNI in session tickets
 | 
				
			||||||
 | 
					        if (enableLogging) {
 | 
				
			||||||
 | 
					          log(`Checking for session ticket information to extract server name...`);
 | 
				
			||||||
 | 
					          // Log more details for debugging
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            // Look at the raw buffer for patterns
 | 
				
			||||||
 | 
					            log(`Buffer hexdump (first 100 bytes): ${buffer.slice(0, 100).toString('hex')}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Try to find hostname-like patterns in the buffer
 | 
				
			||||||
 | 
					            const bufferStr = buffer.toString('utf8', 0, buffer.length);
 | 
				
			||||||
 | 
					            const hostnamePattern =
 | 
				
			||||||
 | 
					              /([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/gi;
 | 
				
			||||||
 | 
					            const hostMatches = bufferStr.match(hostnamePattern);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (hostMatches && hostMatches.length > 0) {
 | 
				
			||||||
 | 
					              log(`Possible hostnames found in buffer: ${hostMatches.join(', ')}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Check if any match looks like a valid domain
 | 
				
			||||||
 | 
					              for (const match of hostMatches) {
 | 
				
			||||||
 | 
					                if (match.includes('.') && match.length > 3) {
 | 
				
			||||||
 | 
					                  log(`Potential SNI found in session data: ${match}`);
 | 
				
			||||||
 | 
					                  // Don't automatically use this - just log for debugging
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } catch (e) {
 | 
				
			||||||
 | 
					            log(`Error scanning for patterns: ${e}`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If we still don't have SNI, check for cached sessions
 | 
				
			||||||
 | 
					        const cachedSni = this.getCachedSession(connectionInfo.sourceIp);
 | 
				
			||||||
 | 
					        if (cachedSni) {
 | 
				
			||||||
 | 
					          log(`Using cached SNI for session resumption: ${cachedSni}`);
 | 
				
			||||||
 | 
					          return cachedSni;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log(`Session resumption without extractable SNI`);
 | 
				
			||||||
 | 
					        // If allowSessionTicket=false, should be rejected by caller
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // For handshake messages, try the full extraction process
 | 
					    // For handshake messages, try the full extraction process
 | 
				
			||||||
    const sni = this.extractSNIWithResumptionSupport(buffer, connectionInfo, enableLogging);
 | 
					    const sni = this.extractSNIWithResumptionSupport(buffer, connectionInfo, enableLogging);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user