/** * Routing Information Extractor * * Extracts minimal routing information from protocols * without full parsing */ import type { IRoutingInfo, IConnectionContext, TProtocolType } from '../../protocols/common/types.js'; import { SniExtraction } from '../../protocols/tls/sni/sni-extraction.js'; import { HttpParser } from '../../protocols/http/index.js'; /** * Extracts routing information from protocol data */ export class RoutingExtractor { /** * Extract routing info based on protocol type */ static extract( data: Buffer, protocol: TProtocolType, context?: IConnectionContext ): IRoutingInfo | null { switch (protocol) { case 'tls': case 'https': return this.extractTlsRouting(data, context); case 'http': return this.extractHttpRouting(data); default: return null; } } /** * Extract routing from TLS ClientHello (SNI) */ private static extractTlsRouting( data: Buffer, context?: IConnectionContext ): IRoutingInfo | null { try { // Quick SNI extraction without full parsing const sni = SniExtraction.extractSNI(data); if (sni) { return { domain: sni, protocol: 'tls', port: 443 // Default HTTPS port }; } return null; } catch (error) { // Extraction failed, return null return null; } } /** * Extract routing from HTTP headers (Host header) */ private static extractHttpRouting(data: Buffer): IRoutingInfo | null { try { // Look for first line const firstLineEnd = data.indexOf('\n'); if (firstLineEnd === -1) { return null; } // Parse request line const firstLine = data.subarray(0, firstLineEnd).toString('ascii').trim(); const requestLine = HttpParser.parseRequestLine(firstLine); if (!requestLine) { return null; } // Look for Host header let pos = firstLineEnd + 1; const maxSearch = Math.min(data.length, 4096); // Don't search too far while (pos < maxSearch) { const lineEnd = data.indexOf('\n', pos); if (lineEnd === -1) break; const line = data.subarray(pos, lineEnd).toString('ascii').trim(); // Empty line means end of headers if (line.length === 0) break; // Check for Host header if (line.toLowerCase().startsWith('host:')) { const hostValue = line.substring(5).trim(); const domain = HttpParser.extractDomainFromHost(hostValue); return { domain, path: requestLine.path, protocol: 'http', port: 80 // Default HTTP port }; } pos = lineEnd + 1; } // No Host header found, but we have the path return { path: requestLine.path, protocol: 'http', port: 80 }; } catch (error) { // Extraction failed return null; } } /** * Try to extract domain from any protocol */ static extractDomain(data: Buffer, hint?: TProtocolType): string | null { // If we have a hint, use it if (hint) { const routing = this.extract(data, hint); return routing?.domain || null; } // Try TLS first (more specific) const tlsRouting = this.extractTlsRouting(data); if (tlsRouting?.domain) { return tlsRouting.domain; } // Try HTTP const httpRouting = this.extractHttpRouting(data); if (httpRouting?.domain) { return httpRouting.domain; } return null; } }