Compare commits

...

10 Commits

Author SHA1 Message Date
60a0ad106d 4.1.10
Some checks failed
Default (tags) / security (push) Successful in 36s
Default (tags) / test (push) Failing after 1m0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-16 14:49:25 +00:00
a70c123007 fix(connectionhandler): Increase delay timings for TLS alert transmission in session ticket blocking to allow graceful socket termination 2025-03-16 14:49:25 +00:00
46aa7620b0 4.1.9
Some checks failed
Default (tags) / security (push) Successful in 35s
Default (tags) / test (push) Failing after 1m1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-16 14:13:36 +00:00
f72db86e37 fix(ConnectionHandler): Replace closeNotify alert with handshake failure alert in TLS ClientHello handling to properly signal missing SNI and enforce session ticket restrictions. 2025-03-16 14:13:35 +00:00
d612df107e 4.1.8
Some checks failed
Default (tags) / security (push) Successful in 30s
Default (tags) / test (push) Failing after 1m1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-16 14:02:18 +00:00
1c34578c36 fix(ConnectionHandler/tls): Change the TLS alert sent when a ClientHello lacks SNI: use the close_notify alert instead of handshake_failure to prompt immediate retry with SNI. 2025-03-16 14:02:18 +00:00
1f9943b5a7 4.1.7
Some checks failed
Default (tags) / security (push) Successful in 34s
Default (tags) / test (push) Failing after 59s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-16 13:47:34 +00:00
67ddf97547 fix(classes.pp.connectionhandler): Improve TLS alert handling in ClientHello when SNI is missing and session tickets are disallowed 2025-03-16 13:47:34 +00:00
8a96b45ece 4.1.6
Some checks failed
Default (tags) / security (push) Successful in 29s
Default (tags) / test (push) Failing after 1m0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-16 13:28:48 +00:00
2b6464acd5 fix(tls): Refine TLS ClientHello handling when allowSessionTicket is false by replacing extensive alert timeout logic with a concise warning alert and short delay, encouraging immediate client retry with proper SNI 2025-03-16 13:28:48 +00:00
4 changed files with 91 additions and 87 deletions

View File

@ -1,5 +1,38 @@
# Changelog
## 2025-03-16 - 4.1.10 - fix(connectionhandler)
Increase delay timings for TLS alert transmission in session ticket blocking to allow graceful socket termination
- Updated finishConnection: replaced immediate socket.destroy with a graceful end call
- Increased delay after successful write from 50ms to 200ms to allow alert processing
- Raised safety timeout from 250ms to 400ms when waiting for 'drain' event
## 2025-03-16 - 4.1.9 - fix(ConnectionHandler)
Replace closeNotify alert with handshake failure alert in TLS ClientHello handling to properly signal missing SNI and enforce session ticket restrictions.
- Switched alert data sent on missing SNI from closeNotifyAlert to sslHandshakeFailureAlertData.
- Ensures consistent TLS alert behavior during handshake failure.
## 2025-03-16 - 4.1.8 - fix(ConnectionHandler/tls)
Change the TLS alert sent when a ClientHello lacks SNI: use the close_notify alert instead of handshake_failure to prompt immediate retry with SNI.
- Replaced the previously sent handshake_failure alert (code 0x28) with a close_notify alert (code 0x00) in the TLS session resumption handling in ConnectionHandler.
- This change encourages clients to immediately retry and include SNI when allowSessionTicket is false.
## 2025-03-16 - 4.1.7 - fix(classes.pp.connectionhandler)
Improve TLS alert handling in ClientHello when SNI is missing and session tickets are disallowed
- Replace the unrecognized_name alert with a handshake_failure alert to ensure better client behavior.
- Refactor the alert sending mechanism using cork/uncork and add a safety timeout for the drain event.
- Enhance logging for debugging TLS handshake failures when SNI is absent.
## 2025-03-16 - 4.1.6 - fix(tls)
Refine TLS ClientHello handling when allowSessionTicket is false by replacing extensive alert timeout logic with a concise warning alert and short delay, encouraging immediate client retry with proper SNI
- Update the TLS alert sending mechanism to use cork/uncork and a short, fixed delay instead of long timeouts
- Remove redundant event listeners and excessive cleanup logic after sending the alert
- Improve code clarity and encourage clients (e.g., Chrome) to retry handshake with SNI more responsively
## 2025-03-16 - 4.1.5 - fix(TLS/ConnectionHandler)
Improve handling of TLS session resumption without SNI by sending an 'unrecognized_name' alert instead of immediately terminating the connection. This change adds a grace period for the client to retry the handshake with proper SNI and cleans up the connection if no valid response is received.

View File

@ -1,6 +1,6 @@
{
"name": "@push.rocks/smartproxy",
"version": "4.1.5",
"version": "4.1.10",
"private": false,
"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.",
"main": "dist_ts/index.js",

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '4.1.5',
version: '4.1.10',
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.'
}

View File

@ -557,13 +557,13 @@ export class ConnectionHandler {
this.tlsManager.isClientHello(chunk) &&
!serverName
) {
// Always block ClientHello without SNI when allowSessionTicket is false
// Block ClientHello without SNI when allowSessionTicket is false
console.log(
`[${connectionId}] No SNI detected in ClientHello and allowSessionTicket=false. ` +
`Sending unrecognized_name alert to encourage client to retry with SNI.`
`Sending warning unrecognized_name alert to encourage immediate retry with SNI.`
);
// Set the termination reason first to avoid races
// Set the termination reason first
if (record.incomingTerminationReason === null) {
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
this.connectionManager.incrementTerminationStat(
@ -572,10 +572,9 @@ export class ConnectionHandler {
);
}
// Send a proper TLS alert before ending the connection
// Using "unrecognized_name" (112) alert which is a warning level alert (1)
// that encourages clients to retry with proper SNI
const alertData = Buffer.from([
// Create a warning-level alert for unrecognized_name
// This encourages Chrome to retry immediately with SNI
const serverNameUnknownAlertData = Buffer.from([
0x15, // Alert record type
0x03,
0x03, // TLS 1.2 version
@ -585,89 +584,61 @@ export class ConnectionHandler {
0x70, // unrecognized_name alert (code 112)
]);
// Send a handshake_failure alert instead of unrecognized_name
const sslHandshakeFailureAlertData = Buffer.from([
0x15, // Alert record type
0x03,
0x03, // TLS 1.2 version
0x00,
0x02, // Length
0x01, // Warning alert level (not fatal)
0x28, // handshake_failure alert (40) instead of unrecognized_name (112)
]);
const closeNotifyAlert = Buffer.from([
0x15, // Alert record type
0x03,
0x03, // TLS 1.2 version
0x00,
0x02, // Length
0x01, // Warning alert level (1)
0x00, // close_notify alert (0)
]);
try {
// Make sure the alert is sent as a single packet
// Use cork/uncork to ensure the alert is sent as a single packet
socket.cork();
socket.write(alertData);
const writeSuccessful = socket.write(serverNameUnknownAlertData);
socket.uncork();
// Set a longer timeout to allow the client to properly handle the alert
// The client might respond by closing the connection or initiating a new handshake
const alertTimeout = setTimeout(() => {
if (!socket.destroyed) {
console.log(`[${connectionId}] Client didn't respond to TLS alert, closing connection gracefully.`);
// Gracefully end the connection
socket.end();
// Only destroy after a delay if it's still hanging
setTimeout(() => {
if (!socket.destroyed) {
console.log(`[${connectionId}] Forcibly closing connection that didn't terminate properly.`);
socket.destroy();
}
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
}, 2000);
}
}, 5000); // Give the client 5 seconds to respond to our alert
// Don't let this timeout keep the process alive
if (alertTimeout.unref) {
alertTimeout.unref();
}
// Handle a proper close from the client
socket.once('close', () => {
clearTimeout(alertTimeout);
console.log(`[${connectionId}] Client closed connection after receiving TLS alert.`);
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni_client_closed');
});
// Also handle if the client sends more data (possibly a new handshake with SNI)
socket.once('data', (newChunk) => {
clearTimeout(alertTimeout);
console.log(`[${connectionId}] Client sent new data after TLS alert, checking for SNI...`);
// Function to handle the clean socket termination - but more gradually
const finishConnection = () => {
// Give Chrome more time to process the alert before closing
// We won't call destroy() at all - just end() and let the socket close naturally
socket.end();
// This would normally be handled by our renegotiation handler,
// but since we're in a special case, we'll check for SNI again
if (this.tlsManager.isTlsHandshake(newChunk) && this.tlsManager.isClientHello(newChunk)) {
const newServerName = this.tlsManager.extractSNI(newChunk, connInfo);
if (newServerName) {
console.log(`[${connectionId}] Client provided SNI in new handshake: ${newServerName}`);
// Update the record
record.incomingTerminationReason = null;
// Remove termination stats increment
// Note: This is a little hacky as we don't have a proper way to decrement stats
// Process the new handshake with SNI
record.lockedDomain = newServerName;
setupConnection(newServerName, newChunk);
return;
} else {
console.log(`[${connectionId}] Client sent new handshake but still without SNI, closing connection.`);
socket.end();
setTimeout(() => {
if (!socket.destroyed) {
socket.destroy();
}
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni_retry_failed');
}, 500);
}
} else {
console.log(`[${connectionId}] Client sent non-handshake data after TLS alert, closing connection.`);
socket.end();
setTimeout(() => {
if (!socket.destroyed) {
socket.destroy();
}
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_invalid_response');
}, 500);
}
});
// Log the cleanup but wait for natural closure
setTimeout(() => {
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
}, 300); // Longer delay to let socket cleanup happen naturally
};
if (writeSuccessful) {
// Wait longer before ending connection to ensure alert is processed by client
setTimeout(finishConnection, 200); // Increased from 50ms to 200ms
} else {
// If the kernel buffer was full, wait for the drain event
socket.once('drain', () => {
// Wait longer after drain as well
setTimeout(finishConnection, 200);
});
// Safety timeout is increased too
setTimeout(() => {
socket.removeAllListeners('drain');
finishConnection();
}, 400); // Increased from 250ms to 400ms
}
} catch (err) {
// If we can't send the alert, fall back to immediate termination
console.log(`[${connectionId}] Error sending TLS alert: ${err.message}`);
@ -1122,4 +1093,4 @@ export class ConnectionHandler {
}
});
}
}
}