diff --git a/changelog.md b/changelog.md index 745bfe5..0ef9102 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2025-02-27 - 3.18.0 - feat(PortProxy) +Add SNI-based renegotiation handling in PortProxy + +- Introduced a new field 'lockedDomain' in IConnectionRecord to store initial SNI. +- Enhanced connection management by enforcing termination if rehandshake is detected with different SNI. + ## 2025-02-27 - 3.17.1 - fix(PortProxy) Fix handling of SNI re-negotiation in PortProxy diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a013b4b..b66d22e 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.17.1', + version: '3.18.0', 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 b46ef42..1f70948 100644 --- a/ts/classes.portproxy.ts +++ b/ts/classes.portproxy.ts @@ -90,6 +90,7 @@ interface IConnectionRecord { outgoing: plugins.net.Socket | null; incomingStartTime: number; outgoingStartTime?: number; + lockedDomain?: string; // New field to lock this connection to the initial SNI connectionClosed: boolean; cleanupTimer?: NodeJS.Timeout; // Timer to force cleanup after max lifetime/inactivity } @@ -366,7 +367,22 @@ export class PortProxy { socket.setTimeout(0); initialDataReceived = true; const serverName = extractSNI(chunk) || ''; + // Lock the connection to the negotiated SNI. + connectionRecord.lockedDomain = serverName; console.log(`Received connection from ${remoteIP} with SNI: ${serverName}`); + // Delay adding the renegotiation listener until the next tick, + // so the initial ClientHello is not reprocessed. + setImmediate(() => { + socket.on('data', (renegChunk: Buffer) => { + if (renegChunk.length > 0 && renegChunk.readUInt8(0) === 22) { + const newSNI = extractSNI(renegChunk); + if (newSNI && newSNI !== connectionRecord.lockedDomain) { + console.log(`Rehandshake detected with different SNI: ${newSNI} vs locked ${connectionRecord.lockedDomain}. Terminating connection.`); + cleanupOnce(); + } + } + }); + }); setupConnection(serverName, chunk); }); } else {