diff --git a/changelog.md b/changelog.md index f33f784..1d73ed9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-03-11 - 3.31.2 - fix(PortProxy) +Improve SNI renegotiation handling by adding flexible domain configuration matching on rehandshake and session resumption events. + +- When a rehandshake is detected with a changed SNI, first check existing domain config rules and log if allowed. +- If the exact domain config is not found, additionally attempt flexible matching using parent domain and wildcard patterns. +- For resumed sessions, try an exact match first and then use fallback logic to select a similar domain config based on matching target IP. +- Enhanced logging added to help diagnose missing or mismatched domain configurations. + ## 2025-03-11 - 3.31.1 - fix(PortProxy) Improve TLS handshake buffering and enhance debug logging for SNI forwarding in PortProxy diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 73da0bd..a693a2a 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '3.31.1', + version: '3.31.2', description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options.' } diff --git a/ts/classes.portproxy.ts b/ts/classes.portproxy.ts index e79d2b1..adc9ef1 100644 --- a/ts/classes.portproxy.ts +++ b/ts/classes.portproxy.ts @@ -1179,15 +1179,46 @@ export class PortProxy { // Allow if the new SNI matches existing domain config or find a new matching config let allowed = false; + // First check if the new SNI is allowed under the existing domain config + // This is the preferred approach as it maintains the existing connection context if (record.domainConfig) { allowed = record.domainConfig.domains.some(d => plugins.minimatch(newSNI, d)); + + if (allowed) { + console.log(`[${connectionId}] Rehandshake SNI ${newSNI} allowed by existing domain config`); + } } + // If not allowed by existing config, try to find an alternative domain config if (!allowed) { - const newDomainConfig = this.settings.domainConfigs.find((config) => + // First try exact match + let newDomainConfig = this.settings.domainConfigs.find((config) => config.domains.some((d) => plugins.minimatch(newSNI, d)) ); + // If no exact match, try flexible matching with domain parts (for wildcard domains) + if (!newDomainConfig) { + console.log(`[${connectionId}] No exact domain config match for rehandshake SNI: ${newSNI}, trying flexible matching`); + + const domainParts = newSNI.split('.'); + + // Try matching with parent domains or wildcard patterns + if (domainParts.length > 2) { + const parentDomain = domainParts.slice(1).join('.'); + const wildcardDomain = '*.' + parentDomain; + + console.log(`[${connectionId}] Trying alternative patterns: ${parentDomain} or ${wildcardDomain}`); + + newDomainConfig = this.settings.domainConfigs.find((config) => + config.domains.some((d) => + d === parentDomain || + d === wildcardDomain || + plugins.minimatch(parentDomain, d) + ) + ); + } + } + if (newDomainConfig) { const effectiveAllowedIPs = [ ...newDomainConfig.allowedIPs, @@ -2002,15 +2033,63 @@ export class PortProxy { // The resumed domain will be in serverName if this is a session resumption if (serverName && connectionRecord.lockedDomain === serverName && serverName !== '') { // Override domain config lookup for session resumption - crucial for certificate selection - const resumedDomainConfig = this.settings.domainConfigs.find((config) => + + // First try an exact match + let resumedDomainConfig = this.settings.domainConfigs.find((config) => config.domains.some((d) => plugins.minimatch(serverName, d)) ); + // If no exact match found, try a more flexible approach using domain parts + if (!resumedDomainConfig) { + console.log(`[${connectionId}] No exact domain config match for resumed domain: ${serverName}, trying flexible matching`); + + // Extract domain parts (e.g., for "sub.example.com" try matching with "*.example.com") + const domainParts = serverName.split('.'); + + // Try matching with parent domains or wildcard patterns + if (domainParts.length > 2) { + const parentDomain = domainParts.slice(1).join('.'); + const wildcardDomain = '*.' + parentDomain; + + console.log(`[${connectionId}] Trying alternative patterns: ${parentDomain} or ${wildcardDomain}`); + + resumedDomainConfig = this.settings.domainConfigs.find((config) => + config.domains.some((d) => + d === parentDomain || + d === wildcardDomain || + plugins.minimatch(parentDomain, d) + ) + ); + } + } + if (resumedDomainConfig) { domainConfig = resumedDomainConfig; - console.log(`[${connectionId}] Using domain config for resumed session: ${serverName}`); + console.log(`[${connectionId}] Found domain config for resumed session: ${serverName} -> ${resumedDomainConfig.domains.join(',')}`); } else { - console.log(`[${connectionId}] WARNING: Cannot find domain config for resumed domain: ${serverName}`); + // As a fallback, use the first domain config with the same target IP if possible + if (domainConfig && domainConfig.targetIPs && domainConfig.targetIPs.length > 0) { + const targetIP = domainConfig.targetIPs[0]; + + const similarConfig = this.settings.domainConfigs.find((config) => + config.targetIPs && config.targetIPs.includes(targetIP) + ); + + if (similarConfig && similarConfig !== domainConfig) { + console.log(`[${connectionId}] Using similar domain config with matching target IP for resumed domain: ${serverName}`); + domainConfig = similarConfig; + } else { + console.log(`[${connectionId}] WARNING: Cannot find domain config for resumed domain: ${serverName}`); + // Log available domains to help diagnose the issue + console.log(`[${connectionId}] Available domains:`, + this.settings.domainConfigs.map(config => config.domains.join(',')).join(' | ')); + } + } else { + console.log(`[${connectionId}] WARNING: Cannot find domain config for resumed domain: ${serverName}`); + // Log available domains to help diagnose the issue + console.log(`[${connectionId}] Available domains:`, + this.settings.domainConfigs.map(config => config.domains.join(',')).join(' | ')); + } } }