148 lines
3.4 KiB
TypeScript
148 lines
3.4 KiB
TypeScript
![]() |
/**
|
||
|
* Quick Protocol Detector
|
||
|
*
|
||
|
* Lightweight protocol identification based on minimal bytes
|
||
|
* No parsing, just identification
|
||
|
*/
|
||
|
|
||
|
import type { IProtocolDetector, IProtocolDetectionResult } from '../../protocols/common/types.js';
|
||
|
import { TlsRecordType } from '../../protocols/tls/index.js';
|
||
|
import { HttpParser } from '../../protocols/http/index.js';
|
||
|
|
||
|
/**
|
||
|
* Quick protocol detector for fast identification
|
||
|
*/
|
||
|
export class QuickProtocolDetector implements IProtocolDetector {
|
||
|
/**
|
||
|
* Check if this detector can handle the data
|
||
|
*/
|
||
|
canHandle(data: Buffer): boolean {
|
||
|
return data.length >= 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Perform quick detection based on first few bytes
|
||
|
*/
|
||
|
quickDetect(data: Buffer): IProtocolDetectionResult {
|
||
|
if (data.length === 0) {
|
||
|
return {
|
||
|
protocol: 'unknown',
|
||
|
confidence: 0,
|
||
|
requiresMoreData: true
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Check for TLS
|
||
|
const tlsResult = this.checkTls(data);
|
||
|
if (tlsResult.confidence > 80) {
|
||
|
return tlsResult;
|
||
|
}
|
||
|
|
||
|
// Check for HTTP
|
||
|
const httpResult = this.checkHttp(data);
|
||
|
if (httpResult.confidence > 80) {
|
||
|
return httpResult;
|
||
|
}
|
||
|
|
||
|
// Need more data or unknown
|
||
|
return {
|
||
|
protocol: 'unknown',
|
||
|
confidence: 0,
|
||
|
requiresMoreData: data.length < 20
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if data looks like TLS
|
||
|
*/
|
||
|
private checkTls(data: Buffer): IProtocolDetectionResult {
|
||
|
if (data.length < 3) {
|
||
|
return {
|
||
|
protocol: 'tls',
|
||
|
confidence: 0,
|
||
|
requiresMoreData: true
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const firstByte = data[0];
|
||
|
const secondByte = data[1];
|
||
|
|
||
|
// Check for valid TLS record type
|
||
|
const validRecordTypes = [
|
||
|
TlsRecordType.CHANGE_CIPHER_SPEC,
|
||
|
TlsRecordType.ALERT,
|
||
|
TlsRecordType.HANDSHAKE,
|
||
|
TlsRecordType.APPLICATION_DATA,
|
||
|
TlsRecordType.HEARTBEAT
|
||
|
];
|
||
|
|
||
|
if (!validRecordTypes.includes(firstByte)) {
|
||
|
return {
|
||
|
protocol: 'tls',
|
||
|
confidence: 0
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Check TLS version byte (0x03 for all TLS/SSL versions)
|
||
|
if (secondByte !== 0x03) {
|
||
|
return {
|
||
|
protocol: 'tls',
|
||
|
confidence: 0
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// High confidence it's TLS
|
||
|
return {
|
||
|
protocol: 'tls',
|
||
|
confidence: 95,
|
||
|
metadata: {
|
||
|
recordType: firstByte
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if data looks like HTTP
|
||
|
*/
|
||
|
private checkHttp(data: Buffer): IProtocolDetectionResult {
|
||
|
if (data.length < 3) {
|
||
|
return {
|
||
|
protocol: 'http',
|
||
|
confidence: 0,
|
||
|
requiresMoreData: true
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Quick check for HTTP methods
|
||
|
const start = data.subarray(0, Math.min(10, data.length)).toString('ascii');
|
||
|
|
||
|
// Check common HTTP methods
|
||
|
const httpMethods = ['GET ', 'POST ', 'PUT ', 'DELETE ', 'HEAD ', 'OPTIONS', 'PATCH ', 'CONNECT', 'TRACE '];
|
||
|
for (const method of httpMethods) {
|
||
|
if (start.startsWith(method)) {
|
||
|
return {
|
||
|
protocol: 'http',
|
||
|
confidence: 95,
|
||
|
metadata: {
|
||
|
method: method.trim()
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if it might be HTTP but need more data
|
||
|
if (HttpParser.isPrintableAscii(data, Math.min(20, data.length))) {
|
||
|
// Could be HTTP, but not sure
|
||
|
return {
|
||
|
protocol: 'http',
|
||
|
confidence: 30,
|
||
|
requiresMoreData: data.length < 20
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
protocol: 'http',
|
||
|
confidence: 0
|
||
|
};
|
||
|
}
|
||
|
}
|