fix(PortProxy): Improve SNI renegotiation handling by adding flexible domain configuration matching on rehandshake and session resumption events.
This commit is contained in:
		| @@ -1,5 +1,13 @@ | |||||||
| # Changelog | # 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) | ## 2025-03-11 - 3.31.1 - fix(PortProxy) | ||||||
| Improve TLS handshake buffering and enhance debug logging for SNI forwarding in PortProxy | Improve TLS handshake buffering and enhance debug logging for SNI forwarding in PortProxy | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,6 @@ | |||||||
|  */ |  */ | ||||||
| export const commitinfo = { | export const commitinfo = { | ||||||
|   name: '@push.rocks/smartproxy', |   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.' |   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.' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1179,15 +1179,46 @@ export class PortProxy { | |||||||
|                   // Allow if the new SNI matches existing domain config or find a new matching config |                   // Allow if the new SNI matches existing domain config or find a new matching config | ||||||
|                   let allowed = false; |                   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) { |                   if (record.domainConfig) { | ||||||
|                     allowed = record.domainConfig.domains.some(d => plugins.minimatch(newSNI, d)); |                     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) { |                   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)) |                       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) { |                     if (newDomainConfig) { | ||||||
|                       const effectiveAllowedIPs = [ |                       const effectiveAllowedIPs = [ | ||||||
|                         ...newDomainConfig.allowedIPs, |                         ...newDomainConfig.allowedIPs, | ||||||
| @@ -2002,15 +2033,63 @@ export class PortProxy { | |||||||
|         // The resumed domain will be in serverName if this is a session resumption |         // The resumed domain will be in serverName if this is a session resumption | ||||||
|         if (serverName && connectionRecord.lockedDomain === serverName && serverName !== '') { |         if (serverName && connectionRecord.lockedDomain === serverName && serverName !== '') { | ||||||
|           // Override domain config lookup for session resumption - crucial for certificate selection |           // 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)) |             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) { |           if (resumedDomainConfig) { | ||||||
|             domainConfig = 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 { | ||||||
|  |             // 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 { |               } else { | ||||||
|                 console.log(`[${connectionId}] WARNING: Cannot find domain config for resumed domain: ${serverName}`); |                 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(' | ')); | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user