Files
waylume_server/lib/services/protocol_blocking_service.dart

250 lines
7.6 KiB
Dart

import 'dart:async';
import 'dart:io';
class Connection {
final String protocol;
final String localIP;
final int localPort;
final String remoteIP;
final int remotePort;
Connection({
required this.protocol,
required this.localIP,
required this.localPort,
required this.remoteIP,
required this.remotePort,
});
String get id => '$protocol:$localIP:$localPort->$remoteIP:$remotePort';
@override
String toString() {
return '$protocol $localIP:$localPort -> $remoteIP:$remotePort';
}
}
class ProtocolBlockingService {
static Timer? _monitorTimer;
static final Set<String> _processedConnections = {};
static void initialize() {
print('Initializing Protocol Blocking Service...');
_startConnectionMonitoring();
}
static void _startConnectionMonitoring() {
print('📡 Starting connection monitoring timer (100ms intervals)...');
_monitorTimer = Timer.periodic(Duration(milliseconds: 100), (_) async {
await _scanForNewConnections();
});
}
static Future<void> _scanForNewConnections() async {
static int scanCount = 0;
scanCount++;
try {
// Monitor both TCP and UDP connections
final tcpResult = await Process.run('ss', ['-tnp', 'state', 'syn-sent,established']);
final udpResult = await Process.run('ss', ['-unp']);
if (scanCount % 100 == 0) { // Log every 10 seconds
print('🔍 Scan #$scanCount - TCP exit code: ${tcpResult.exitCode}, UDP exit code: ${udpResult.exitCode}');
}
if (tcpResult.exitCode != 0) {
print('❌ TCP ss command failed: ${tcpResult.stderr}');
return;
}
if (udpResult.exitCode != 0) {
print('❌ UDP ss command failed: ${udpResult.stderr}');
return;
}
final tcpConnections = _parseConnections(tcpResult.stdout.toString(), 'tcp');
final udpConnections = _parseConnections(udpResult.stdout.toString(), 'udp');
if (scanCount % 100 == 0) {
print('🔍 Found ${tcpConnections.length} TCP + ${udpConnections.length} UDP connections');
}
for (final conn in [...tcpConnections, ...udpConnections]) {
if (_isNewConnection(conn)) {
await _handleNewConnection(conn);
}
}
} catch (e) {
print('❌ Error scanning for connections: $e');
}
}
static List<Connection> _parseConnections(String output, String protocol) {
final connections = <Connection>[];
final lines = output.split('\n');
for (final line in lines) {
try {
final conn = _parseConnectionLine(line.trim(), protocol);
if (conn != null) {
connections.add(conn);
}
} catch (e) {
// Skip malformed lines
}
}
return connections;
}
static Connection? _parseConnectionLine(String line, String protocol) {
if (line.isEmpty || line.startsWith('Netid') || line.startsWith('State')) {
return null;
}
// Split by whitespace and filter empty strings
final parts = line.split(RegExp(r'\s+')).where((s) => s.isNotEmpty).toList();
if (parts.length < 4) {
return null;
}
try {
// For TCP: State Recv-Q Send-Q Local_Address:Port Peer_Address:Port
// For UDP: State Recv-Q Send-Q Local_Address:Port Peer_Address:Port
final localAddr = parts[3];
final remoteAddr = parts.length > 4 ? parts[4] : '';
if (remoteAddr.isEmpty || remoteAddr == '*:*') {
return null; // Skip listening sockets
}
final localParts = localAddr.split(':');
final remoteParts = remoteAddr.split(':');
if (localParts.length < 2 || remoteParts.length < 2) {
return null;
}
final localIP = localParts.sublist(0, localParts.length - 1).join(':');
final localPort = int.parse(localParts.last);
final remoteIP = remoteParts.sublist(0, remoteParts.length - 1).join(':');
final remotePort = int.parse(remoteParts.last);
return Connection(
protocol: protocol,
localIP: localIP,
localPort: localPort,
remoteIP: remoteIP,
remotePort: remotePort,
);
} catch (e) {
return null;
}
}
static bool _isNewConnection(Connection conn) {
final id = conn.id;
if (_processedConnections.contains(id)) {
return false;
}
_processedConnections.add(id);
// Clean up old connections periodically (keep last 1000)
if (_processedConnections.length > 1000) {
final toRemove = _processedConnections.length - 800;
final oldConnections = _processedConnections.take(toRemove).toList();
for (final old in oldConnections) {
_processedConnections.remove(old);
}
}
return true;
}
static Future<void> _handleNewConnection(Connection conn) async {
print('🔍 New connection detected: $conn');
try {
// Attempt to capture handshake data
await _captureHandshake(conn);
} catch (e) {
print('⚠️ Error capturing handshake for $conn: $e');
}
}
static Future<void> _captureHandshake(Connection conn) async {
try {
print('📡 Capturing handshake for: $conn');
// Use tcpdump to capture only the first data packet (handshake)
final protocol = conn.protocol;
final process = await Process.start('timeout', [
'2', // 2 second timeout
'tcpdump',
'-i', 'any',
'-c', '1', // Capture only 1 packet
'-s', '200', // Capture first 200 bytes only
'-x', // Output in hex
'$protocol and host ${conn.remoteIP} and port ${conn.remotePort}',
]);
final handshakeData = <String>[];
await for (final data in process.stdout) {
handshakeData.add(String.fromCharCodes(data));
}
final exitCode = await process.exitCode;
if (exitCode == 0 && handshakeData.isNotEmpty) {
final data = handshakeData.join();
print('✅ Handshake captured for $conn:');
print(' Data (first 200 chars): ${data.length > 200 ? data.substring(0, 200) + "..." : data}');
// Analyze the handshake
_analyzeHandshake(data, conn);
} else if (exitCode == 124) {
print('⏱️ Handshake capture timeout for $conn (no packets in 2s)');
} else {
print('❌ Failed to capture handshake for $conn (exit code: $exitCode)');
}
process.kill();
} catch (e) {
print('💥 Error in handshake capture: $e');
}
}
static void _analyzeHandshake(String handshakeData, Connection conn) {
// Simple pattern detection for now
final data = handshakeData.toLowerCase();
String? detectedProtocol;
if (data.contains('bittorrent protocol') || data.contains('13426974546f7272656e742070726f746f636f6c')) {
detectedProtocol = 'BitTorrent';
} else if (data.contains('ssh-2.0') || data.contains('ssh-1.')) {
detectedProtocol = 'SSH';
} else if (data.contains('get ') || data.contains('post ') || data.contains('http/')) {
detectedProtocol = 'HTTP';
} else if (data.contains('220 ') && conn.remotePort == 25) {
detectedProtocol = 'SMTP';
} else if (data.contains('220 ') && conn.remotePort == 21) {
detectedProtocol = 'FTP';
}
if (detectedProtocol != null) {
print('🎯 PROTOCOL DETECTED: $detectedProtocol for $conn');
} else {
print('❓ Unknown protocol for $conn');
}
}
static void dispose() {
_monitorTimer?.cancel();
_monitorTimer = null;
_processedConnections.clear();
print('Protocol Blocking Service disposed');
}
}