/**
 * TLS protocol detector
 */
import { readUInt16BE } from '../utils/buffer-utils.js';
import { tlsVersionToString } from '../utils/parser-utils.js';
// Import from protocols
import { TlsRecordType, TlsHandshakeType, TlsExtensionType } from '../../protocols/tls/index.js';
// Import TLS utilities for SNI extraction from protocols
import { SniExtraction } from '../../protocols/tls/sni/sni-extraction.js';
import { ClientHelloParser } from '../../protocols/tls/sni/client-hello-parser.js';
/**
 * TLS detector implementation
 */
export class TlsDetector {
    /**
     * Minimum bytes needed to identify TLS (record header)
     */
    static { this.MIN_TLS_HEADER_SIZE = 5; }
    /**
     * Detect TLS protocol from buffer
     */
    detect(buffer, options) {
        // Check if buffer is too small
        if (buffer.length < TlsDetector.MIN_TLS_HEADER_SIZE) {
            return null;
        }
        // Check if this is a TLS record
        if (!this.isTlsRecord(buffer)) {
            return null;
        }
        // Extract basic TLS info
        const recordType = buffer[0];
        const tlsMajor = buffer[1];
        const tlsMinor = buffer[2];
        const recordLength = readUInt16BE(buffer, 3);
        // Initialize connection info
        const connectionInfo = {
            protocol: 'tls',
            tlsVersion: tlsVersionToString(tlsMajor, tlsMinor) || undefined
        };
        // If it's a handshake, try to extract more info
        if (recordType === TlsRecordType.HANDSHAKE && buffer.length >= 6) {
            const handshakeType = buffer[5];
            // For ClientHello, extract SNI and other info
            if (handshakeType === TlsHandshakeType.CLIENT_HELLO) {
                // Check if we have the complete handshake
                const totalRecordLength = recordLength + 5; // Including TLS header
                if (buffer.length >= totalRecordLength) {
                    // Extract SNI using existing logic
                    const sni = SniExtraction.extractSNI(buffer);
                    if (sni) {
                        connectionInfo.domain = sni;
                        connectionInfo.sni = sni;
                    }
                    // Parse ClientHello for additional info
                    const parseResult = ClientHelloParser.parseClientHello(buffer);
                    if (parseResult.isValid) {
                        // Extract ALPN if present
                        const alpnExtension = parseResult.extensions.find(ext => ext.type === TlsExtensionType.APPLICATION_LAYER_PROTOCOL_NEGOTIATION);
                        if (alpnExtension) {
                            connectionInfo.alpn = this.parseAlpnExtension(alpnExtension.data);
                        }
                        // Store cipher suites if needed
                        if (parseResult.cipherSuites && options?.extractFullHeaders) {
                            connectionInfo.cipherSuites = this.parseCipherSuites(parseResult.cipherSuites);
                        }
                    }
                    // Return complete result
                    return {
                        protocol: 'tls',
                        connectionInfo,
                        remainingBuffer: buffer.length > totalRecordLength
                            ? buffer.subarray(totalRecordLength)
                            : undefined,
                        isComplete: true
                    };
                }
                else {
                    // Incomplete handshake
                    return {
                        protocol: 'tls',
                        connectionInfo,
                        isComplete: false,
                        bytesNeeded: totalRecordLength
                    };
                }
            }
        }
        // For other TLS record types, just return basic info
        return {
            protocol: 'tls',
            connectionInfo,
            isComplete: true,
            remainingBuffer: buffer.length > recordLength + 5
                ? buffer.subarray(recordLength + 5)
                : undefined
        };
    }
    /**
     * Check if buffer can be handled by this detector
     */
    canHandle(buffer) {
        return buffer.length >= TlsDetector.MIN_TLS_HEADER_SIZE &&
            this.isTlsRecord(buffer);
    }
    /**
     * Get minimum bytes needed for detection
     */
    getMinimumBytes() {
        return TlsDetector.MIN_TLS_HEADER_SIZE;
    }
    /**
     * Check if buffer contains a valid TLS record
     */
    isTlsRecord(buffer) {
        const recordType = buffer[0];
        // Check for valid record type
        const validTypes = [
            TlsRecordType.CHANGE_CIPHER_SPEC,
            TlsRecordType.ALERT,
            TlsRecordType.HANDSHAKE,
            TlsRecordType.APPLICATION_DATA,
            TlsRecordType.HEARTBEAT
        ];
        if (!validTypes.includes(recordType)) {
            return false;
        }
        // Check TLS version bytes (should be 0x03 0x0X)
        if (buffer[1] !== 0x03) {
            return false;
        }
        // Check record length is reasonable
        const recordLength = readUInt16BE(buffer, 3);
        if (recordLength > 16384) { // Max TLS record size
            return false;
        }
        return true;
    }
    /**
     * Parse ALPN extension data
     */
    parseAlpnExtension(data) {
        const protocols = [];
        if (data.length < 2) {
            return protocols;
        }
        const listLength = readUInt16BE(data, 0);
        let offset = 2;
        while (offset < Math.min(2 + listLength, data.length)) {
            const protoLength = data[offset];
            offset++;
            if (offset + protoLength <= data.length) {
                const protocol = data.subarray(offset, offset + protoLength).toString('ascii');
                protocols.push(protocol);
                offset += protoLength;
            }
            else {
                break;
            }
        }
        return protocols;
    }
    /**
     * Parse cipher suites
     */
    parseCipherSuites(cipherData) {
        const suites = [];
        for (let i = 0; i < cipherData.length - 1; i += 2) {
            const suite = readUInt16BE(cipherData, i);
            suites.push(suite);
        }
        return suites;
    }
    /**
     * Detect with context for fragmented data
     */
    detectWithContext(buffer, _context, options) {
        // This method is deprecated - TLS detection should use the fragment manager
        // from the parent detector system, not maintain its own fragments
        return this.detect(buffer, options);
    }
}
//# sourceMappingURL=data:application/json;base64, 