@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
JavaScript
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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