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.

191 lines 13.9 kB
import { Buffer } from 'buffer'; import { TlsRecordType, TlsHandshakeType, TlsExtensionType, TlsUtils } from '../../protocols/tls/utils/tls-utils.js'; import { ClientHelloParser } from '../../protocols/tls/sni/client-hello-parser.js'; import { SniExtraction } from '../../protocols/tls/sni/sni-extraction.js'; /** * SNI (Server Name Indication) handler for TLS connections. * Provides robust extraction of SNI values from TLS ClientHello messages * with support for fragmented packets, TLS 1.3 resumption, Chrome-specific * connection behaviors, and tab hibernation/reactivation scenarios. * * This class retains the original API but leverages the new modular implementation * for better maintainability and testability. */ export class SniHandler { // Re-export constants for backward compatibility static { this.TLS_HANDSHAKE_RECORD_TYPE = TlsRecordType.HANDSHAKE; } static { this.TLS_APPLICATION_DATA_TYPE = TlsRecordType.APPLICATION_DATA; } static { this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE = TlsHandshakeType.CLIENT_HELLO; } static { this.TLS_SNI_EXTENSION_TYPE = TlsExtensionType.SERVER_NAME; } static { this.TLS_SESSION_TICKET_EXTENSION_TYPE = TlsExtensionType.SESSION_TICKET; } static { this.TLS_SNI_HOST_NAME_TYPE = 0; } // NameType.HOST_NAME in RFC 6066 static { this.TLS_PSK_EXTENSION_TYPE = TlsExtensionType.PRE_SHARED_KEY; } static { this.TLS_PSK_KE_MODES_EXTENSION_TYPE = TlsExtensionType.PSK_KEY_EXCHANGE_MODES; } static { this.TLS_EARLY_DATA_EXTENSION_TYPE = TlsExtensionType.EARLY_DATA; } /** * Checks if a buffer contains a TLS handshake message (record type 22) * @param buffer - The buffer to check * @returns true if the buffer starts with a TLS handshake record type */ static isTlsHandshake(buffer) { return TlsUtils.isTlsHandshake(buffer); } /** * Checks if a buffer contains TLS application data (record type 23) * @param buffer - The buffer to check * @returns true if the buffer starts with a TLS application data record type */ static isTlsApplicationData(buffer) { return TlsUtils.isTlsApplicationData(buffer); } /** * Creates a connection ID based on source/destination information * Used to track fragmented ClientHello messages across multiple packets * * @param connectionInfo - Object containing connection identifiers (IP/port) * @returns A string ID for the connection */ static createConnectionId(connectionInfo) { return TlsUtils.createConnectionId(connectionInfo); } /** * Handles potential fragmented ClientHello messages by buffering and reassembling * TLS record fragments that might span multiple TCP packets. * * @param buffer - The current buffer fragment * @param connectionId - Unique identifier for the connection * @param enableLogging - Whether to enable logging * @returns A complete buffer if reassembly is successful, or undefined if more fragments are needed */ static handleFragmentedClientHello(buffer, connectionId, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[SNI Fragment] ${message}`) : undefined; return ClientHelloParser.handleFragmentedClientHello(buffer, connectionId, logger); } /** * Checks if a buffer contains a TLS ClientHello message * @param buffer - The buffer to check * @returns true if the buffer appears to be a ClientHello message */ static isClientHello(buffer) { return TlsUtils.isClientHello(buffer); } /** * Checks if a ClientHello message contains session resumption indicators * such as session tickets or PSK (Pre-Shared Key) extensions. * * @param buffer - The buffer containing a ClientHello message * @param enableLogging - Whether to enable logging * @returns Object containing details about session resumption and SNI presence */ static hasSessionResumption(buffer, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[Session Resumption] ${message}`) : undefined; return ClientHelloParser.hasSessionResumption(buffer, logger); } /** * Detects characteristics of a tab reactivation TLS handshake * These often have specific patterns in Chrome and other browsers * * @param buffer - The buffer containing a ClientHello message * @param enableLogging - Whether to enable logging * @returns true if this appears to be a tab reactivation handshake */ static isTabReactivationHandshake(buffer, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[Tab Reactivation] ${message}`) : undefined; return ClientHelloParser.isTabReactivationHandshake(buffer, logger); } /** * Extracts the SNI (Server Name Indication) from a TLS ClientHello message. * Implements robust parsing with support for session resumption edge cases. * * @param buffer - The buffer containing the TLS ClientHello message * @param enableLogging - Whether to enable detailed debug logging * @returns The extracted server name or undefined if not found */ static extractSNI(buffer, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[SNI Extraction] ${message}`) : undefined; return SniExtraction.extractSNI(buffer, logger); } /** * Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello. * * In TLS 1.3, when a client attempts to resume a session, it may include * the server name in the PSK identity hint rather than in the SNI extension. * * @param buffer - The buffer containing the TLS ClientHello message * @param enableLogging - Whether to enable detailed debug logging * @returns The extracted server name or undefined if not found */ static extractSNIFromPSKExtension(buffer, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[PSK-SNI Extraction] ${message}`) : undefined; return SniExtraction.extractSNIFromPSKExtension(buffer, logger); } /** * Checks if the buffer contains TLS 1.3 early data (0-RTT) * @param buffer - The buffer to check * @param enableLogging - Whether to enable logging * @returns true if early data is detected */ static hasEarlyData(buffer, enableLogging = false) { // This functionality has been moved to ClientHelloParser // We can implement it in terms of the parse result if needed const logger = enableLogging ? (message) => console.log(`[Early Data] ${message}`) : undefined; const parseResult = ClientHelloParser.parseClientHello(buffer, logger); return parseResult.isValid && parseResult.hasEarlyData; } /** * Attempts to extract SNI from an initial ClientHello packet and handles * session resumption edge cases more robustly than the standard extraction. * * This method handles: * 1. Standard SNI extraction * 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.) * 3. Session ticket-based resumption * 4. Fragmented ClientHello messages * 5. TLS 1.3 Early Data (0-RTT) * 6. Chrome's connection racing behaviors * * @param buffer - The buffer containing the TLS ClientHello message * @param connectionInfo - Optional connection information for fragment handling * @param enableLogging - Whether to enable detailed debug logging * @returns The extracted server name or undefined if not found or more data needed */ static extractSNIWithResumptionSupport(buffer, connectionInfo, enableLogging = false) { const logger = enableLogging ? (message) => console.log(`[SNI Extraction] ${message}`) : undefined; return SniExtraction.extractSNIWithResumptionSupport(buffer, connectionInfo, logger); } /** * Main entry point for SNI extraction that handles all edge cases. * This should be called for each TLS packet received from a client. * * The method uses connection tracking to handle fragmented ClientHello * messages and various TLS 1.3 behaviors, including Chrome's connection * racing patterns and tab reactivation behaviors. * * @param buffer - The buffer containing TLS data * @param connectionInfo - Connection metadata (IPs and ports) * @param enableLogging - Whether to enable detailed debug logging * @param cachedSni - Optional cached SNI from previous connections (for racing detection) * @returns The extracted server name or undefined if not found or more data needed */ static processTlsPacket(buffer, connectionInfo, enableLogging = false, cachedSni) { const logger = enableLogging ? (message) => console.log(`[TLS Packet] ${message}`) : undefined; return SniExtraction.processTlsPacket(buffer, connectionInfo, logger, cachedSni); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic25pLWhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy90bHMvc25pL3NuaS1oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDaEMsT0FBTyxFQUNMLGFBQWEsRUFDYixnQkFBZ0IsRUFDaEIsZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDVCxNQUFNLHdDQUF3QyxDQUFDO0FBQ2hELE9BQU8sRUFDTCxpQkFBaUIsRUFFbEIsTUFBTSxnREFBZ0QsQ0FBQztBQUN4RCxPQUFPLEVBQ0wsYUFBYSxFQUVkLE1BQU0sMkNBQTJDLENBQUM7QUFFbkQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUNyQixpREFBaUQ7YUFDekIsOEJBQXlCLEdBQUcsYUFBYSxDQUFDLFNBQVMsQ0FBQzthQUNwRCw4QkFBeUIsR0FBRyxhQUFhLENBQUMsZ0JBQWdCLENBQUM7YUFDM0Qsb0NBQStCLEdBQUcsZ0JBQWdCLENBQUMsWUFBWSxDQUFDO2FBQ2hFLDJCQUFzQixHQUFHLGdCQUFnQixDQUFDLFdBQVcsQ0FBQzthQUN0RCxzQ0FBaUMsR0FBRyxnQkFBZ0IsQ0FBQyxjQUFjLENBQUM7YUFDcEUsMkJBQXNCLEdBQUcsQ0FBQyxDQUFDLEdBQUMsaUNBQWlDO2FBQzdELDJCQUFzQixHQUFHLGdCQUFnQixDQUFDLGNBQWMsQ0FBQzthQUN6RCxvQ0FBK0IsR0FBRyxnQkFBZ0IsQ0FBQyxzQkFBc0IsQ0FBQzthQUMxRSxrQ0FBNkIsR0FBRyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUM7SUFFcEY7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxjQUFjLENBQUMsTUFBYztRQUN6QyxPQUFPLFFBQVEsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsb0JBQW9CLENBQUMsTUFBYztRQUMvQyxPQUFP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