UNPKG

@push.rocks/smartproxy

Version:

A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.

226 lines 19.6 kB
import * as plugins from '../../../plugins.js'; import { TlsAlertLevel, TlsAlertDescription, TlsVersion } from '../utils/tls-utils.js'; /** * TlsAlert class for creating and sending TLS alert messages */ export class TlsAlert { // Use enum values from TlsAlertLevel static { this.LEVEL_WARNING = TlsAlertLevel.WARNING; } static { this.LEVEL_FATAL = TlsAlertLevel.FATAL; } // Use enum values from TlsAlertDescription static { this.CLOSE_NOTIFY = TlsAlertDescription.CLOSE_NOTIFY; } static { this.UNEXPECTED_MESSAGE = TlsAlertDescription.UNEXPECTED_MESSAGE; } static { this.BAD_RECORD_MAC = TlsAlertDescription.BAD_RECORD_MAC; } static { this.DECRYPTION_FAILED = TlsAlertDescription.DECRYPTION_FAILED; } static { this.RECORD_OVERFLOW = TlsAlertDescription.RECORD_OVERFLOW; } static { this.DECOMPRESSION_FAILURE = TlsAlertDescription.DECOMPRESSION_FAILURE; } static { this.HANDSHAKE_FAILURE = TlsAlertDescription.HANDSHAKE_FAILURE; } static { this.NO_CERTIFICATE = TlsAlertDescription.NO_CERTIFICATE; } static { this.BAD_CERTIFICATE = TlsAlertDescription.BAD_CERTIFICATE; } static { this.UNSUPPORTED_CERTIFICATE = TlsAlertDescription.UNSUPPORTED_CERTIFICATE; } static { this.CERTIFICATE_REVOKED = TlsAlertDescription.CERTIFICATE_REVOKED; } static { this.CERTIFICATE_EXPIRED = TlsAlertDescription.CERTIFICATE_EXPIRED; } static { this.CERTIFICATE_UNKNOWN = TlsAlertDescription.CERTIFICATE_UNKNOWN; } static { this.ILLEGAL_PARAMETER = TlsAlertDescription.ILLEGAL_PARAMETER; } static { this.UNKNOWN_CA = TlsAlertDescription.UNKNOWN_CA; } static { this.ACCESS_DENIED = TlsAlertDescription.ACCESS_DENIED; } static { this.DECODE_ERROR = TlsAlertDescription.DECODE_ERROR; } static { this.DECRYPT_ERROR = TlsAlertDescription.DECRYPT_ERROR; } static { this.EXPORT_RESTRICTION = TlsAlertDescription.EXPORT_RESTRICTION; } static { this.PROTOCOL_VERSION = TlsAlertDescription.PROTOCOL_VERSION; } static { this.INSUFFICIENT_SECURITY = TlsAlertDescription.INSUFFICIENT_SECURITY; } static { this.INTERNAL_ERROR = TlsAlertDescription.INTERNAL_ERROR; } static { this.INAPPROPRIATE_FALLBACK = TlsAlertDescription.INAPPROPRIATE_FALLBACK; } static { this.USER_CANCELED = TlsAlertDescription.USER_CANCELED; } static { this.NO_RENEGOTIATION = TlsAlertDescription.NO_RENEGOTIATION; } static { this.MISSING_EXTENSION = TlsAlertDescription.MISSING_EXTENSION; } static { this.UNSUPPORTED_EXTENSION = TlsAlertDescription.UNSUPPORTED_EXTENSION; } static { this.CERTIFICATE_REQUIRED = TlsAlertDescription.CERTIFICATE_REQUIRED; } static { this.UNRECOGNIZED_NAME = TlsAlertDescription.UNRECOGNIZED_NAME; } static { this.BAD_CERTIFICATE_STATUS_RESPONSE = TlsAlertDescription.BAD_CERTIFICATE_STATUS_RESPONSE; } static { this.BAD_CERTIFICATE_HASH_VALUE = TlsAlertDescription.BAD_CERTIFICATE_HASH_VALUE; } static { this.UNKNOWN_PSK_IDENTITY = TlsAlertDescription.UNKNOWN_PSK_IDENTITY; } static { this.CERTIFICATE_REQUIRED_1_3 = TlsAlertDescription.CERTIFICATE_REQUIRED_1_3; } static { this.NO_APPLICATION_PROTOCOL = TlsAlertDescription.NO_APPLICATION_PROTOCOL; } /** * Create a TLS alert buffer with the specified level and description code * * @param level Alert level (warning or fatal) * @param description Alert description code * @param tlsVersion TLS version bytes (default is TLS 1.2: 0x0303) * @returns Buffer containing the TLS alert message */ static create(level, description, tlsVersion = [TlsVersion.TLS1_2[0], TlsVersion.TLS1_2[1]]) { return Buffer.from([ 0x15, // Alert record type tlsVersion[0], tlsVersion[1], // TLS version (default to TLS 1.2: 0x0303) 0x00, 0x02, // Length level, // Alert level description, // Alert description ]); } /** * Create a warning-level TLS alert * * @param description Alert description code * @returns Buffer containing the warning-level TLS alert message */ static createWarning(description) { return this.create(this.LEVEL_WARNING, description); } /** * Create a fatal-level TLS alert * * @param description Alert description code * @returns Buffer containing the fatal-level TLS alert message */ static createFatal(description) { return this.create(this.LEVEL_FATAL, description); } /** * Send a TLS alert to a socket and optionally close the connection * * @param socket The socket to send the alert to * @param level Alert level (warning or fatal) * @param description Alert description code * @param closeAfterSend Whether to close the connection after sending the alert * @param closeDelay Milliseconds to wait before closing the connection (default: 200ms) * @returns Promise that resolves when the alert has been sent */ static async send(socket, level, description, closeAfterSend = false, closeDelay = 200) { const alert = this.create(level, description); return new Promise((resolve, reject) => { try { // Ensure the alert is written as a single packet socket.cork(); const writeSuccessful = socket.write(alert, (err) => { if (err) { reject(err); return; } if (closeAfterSend) { setTimeout(() => { socket.end(); resolve(); }, closeDelay); } else { resolve(); } }); socket.uncork(); // If write wasn't successful immediately, wait for drain if (!writeSuccessful && !closeAfterSend) { socket.once('drain', () => { resolve(); }); } } catch (err) { reject(err); } }); } /** * Pre-defined TLS alert messages */ static { this.alerts = { // Warning level alerts closeNotify: TlsAlert.createWarning(TlsAlert.CLOSE_NOTIFY), unsupportedExtension: TlsAlert.createWarning(TlsAlert.UNSUPPORTED_EXTENSION), certificateRequired: TlsAlert.createWarning(TlsAlert.CERTIFICATE_REQUIRED), unrecognizedName: TlsAlert.createWarning(TlsAlert.UNRECOGNIZED_NAME), noRenegotiation: TlsAlert.createWarning(TlsAlert.NO_RENEGOTIATION), userCanceled: TlsAlert.createWarning(TlsAlert.USER_CANCELED), // Warning level alerts for session resumption certificateExpiredWarning: TlsAlert.createWarning(TlsAlert.CERTIFICATE_EXPIRED), handshakeFailureWarning: TlsAlert.createWarning(TlsAlert.HANDSHAKE_FAILURE), insufficientSecurityWarning: TlsAlert.createWarning(TlsAlert.INSUFFICIENT_SECURITY), // Fatal level alerts unexpectedMessage: TlsAlert.createFatal(TlsAlert.UNEXPECTED_MESSAGE), badRecordMac: TlsAlert.createFatal(TlsAlert.BAD_RECORD_MAC), recordOverflow: TlsAlert.createFatal(TlsAlert.RECORD_OVERFLOW), handshakeFailure: TlsAlert.createFatal(TlsAlert.HANDSHAKE_FAILURE), badCertificate: TlsAlert.createFatal(TlsAlert.BAD_CERTIFICATE), certificateExpired: TlsAlert.createFatal(TlsAlert.CERTIFICATE_EXPIRED), certificateUnknown: TlsAlert.createFatal(TlsAlert.CERTIFICATE_UNKNOWN), illegalParameter: TlsAlert.createFatal(TlsAlert.ILLEGAL_PARAMETER), unknownCA: TlsAlert.createFatal(TlsAlert.UNKNOWN_CA), accessDenied: TlsAlert.createFatal(TlsAlert.ACCESS_DENIED), decodeError: TlsAlert.createFatal(TlsAlert.DECODE_ERROR), decryptError: TlsAlert.createFatal(TlsAlert.DECRYPT_ERROR), protocolVersion: TlsAlert.createFatal(TlsAlert.PROTOCOL_VERSION), insufficientSecurity: TlsAlert.createFatal(TlsAlert.INSUFFICIENT_SECURITY), internalError: TlsAlert.createFatal(TlsAlert.INTERNAL_ERROR), unrecognizedNameFatal: TlsAlert.createFatal(TlsAlert.UNRECOGNIZED_NAME), }; } /** * Utility method to send a warning-level unrecognized_name alert * Specifically designed for SNI issues to encourage the client to retry with SNI * * @param socket The socket to send the alert to * @returns Promise that resolves when the alert has been sent */ static async sendSniRequired(socket) { return this.send(socket, this.LEVEL_WARNING, this.UNRECOGNIZED_NAME); } /** * Utility method to send a close_notify alert and close the connection * * @param socket The socket to send the alert to * @param closeDelay Milliseconds to wait before closing the connection (default: 200ms) * @returns Promise that resolves when the alert has been sent and the connection closed */ static async sendCloseNotify(socket, closeDelay = 200) { return this.send(socket, this.LEVEL_WARNING, this.CLOSE_NOTIFY, true, closeDelay); } /** * Utility method to send a certificate_expired alert to force new TLS session * * @param socket The socket to send the alert to * @param fatal Whether to send as a fatal alert (default: false) * @param closeAfterSend Whether to close the connection after sending the alert (default: true) * @param closeDelay Milliseconds to wait before closing the connection (default: 200ms) * @returns Promise that resolves when the alert has been sent */ static async sendCertificateExpired(socket, fatal = false, closeAfterSend = true, closeDelay = 200) { const level = fatal ? this.LEVEL_FATAL : this.LEVEL_WARNING; return this.send(socket, level, this.CERTIFICATE_EXPIRED, closeAfterSend, closeDelay); } /** * Send a sequence of alerts to force SNI from clients * This combines multiple alerts to ensure maximum browser compatibility * * @param socket The socket to send the alerts to * @returns Promise that resolves when all alerts have been sent */ static async sendForceSniSequence(socket) { try { // Send unrecognized_name (warning) socket.cork(); socket.write(this.alerts.unrecognizedName); socket.uncork(); // Give the socket time to send the alert return new Promise((resolve) => { setTimeout(resolve, 50); }); } catch (err) { return Promise.reject(err); } } /** * Send a fatal level alert that immediately terminates the connection * * @param socket The socket to send the alert to * @param description Alert description code * @param closeDelay Milliseconds to wait before closing the connection (default: 100ms) * @returns Promise that resolves when the alert has been sent and the connection closed */ static async sendFatalAndClose(socket, description, closeDelay = 100) { return this.send(socket, this.LEVEL_FATAL, description, true, closeDelay); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGxzLWFsZXJ0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvcHJvdG9jb2xzL3Rscy9hbGVydHMvdGxzLWFsZXJ0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGFBQWEsRUFBRSxtQkFBbUIsRUFBRSxVQUFVLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUV2Rjs7R0FFRztBQUNILE1BQU0sT0FBTyxRQUFRO0lBQ25CLHFDQUFxQzthQUNyQixrQkFBYSxHQUFHLGFBQWEsQ0FBQyxPQUFPLENBQUM7YUFDdEMsZ0JBQVcsR0FBRyxhQUFhLENBQUMsS0FBSyxDQUFDO0lBRWxELDJDQUEyQzthQUMzQixpQkFBWSxHQUFHLG1CQUFtQixDQUFDLFlBQVksQ0FBQzthQUNoRCx1QkFBa0IsR0FBRyxtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FBQzthQUM1RCxtQkFBYyxHQUFHLG1CQUFtQixDQUFDLGNBQWMsQ0FBQzthQUNwRCxzQkFBaUIsR0FBRyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQzthQUMxRCxvQkFBZSxHQUFHLG1CQUFtQixDQUFDLGVBQWUsQ0FBQzthQUN0RCwwQkFBcUIsR0FBRyxtQkFBbUIsQ0FBQyxxQkFBcUIsQ0FBQzthQUNsRSxzQkFBaUIsR0FBRyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQzthQUMxRCxtQkFBYyxHQUFHLG1CQUFtQixDQUFDLGNBQWMsQ0FBQzthQUNwRCxvQkFBZSxHQUFHLG1CQUFtQixDQUFDLGVBQWUsQ0FBQzthQUN0RCw0QkFBdUIsR0FBRyxtQkFBbUIsQ0FBQyx1QkFBdUIsQ0FBQzthQUN0RSx3QkFBbUIsR0FBRyxtQkFBbUIsQ0FBQyxtQkFBbUIsQ0FBQzthQUM5RCx3QkFBbUIsR0FBRyxtQkFBbUIsQ0FBQyxtQkFBbUIsQ0FBQzthQUM5RCx3QkFBbUIsR0FBRyxtQkFBbUIsQ0FBQyxtQkFBbUIsQ0FB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