Status Window

This commit is contained in:
Joerg Dorgeist
2026-01-14 11:57:57 +01:00
parent 16be5a95ec
commit 97487ce060
3 changed files with 406 additions and 2 deletions

View File

@@ -12,6 +12,17 @@ var serverSocketId = null;
var isListening = false;
var isInitialized = false; // Prevent double initialization
var handlersSetup = false; // Prevent duplicate event listeners
var appWindow = null; // Keep app window reference to prevent suspension
// Connection statistics
var stats = {
totalConnections: 0,
activeConnections: 0,
bytesReceived: 0,
bytesSent: 0,
startTime: Date.now(),
connections: {} // socketId -> { connectTime, bytesReceived, bytesSent }
};
// SOCKS5 Connection States
var ConnectionState = {
@@ -78,7 +89,18 @@ function initConnection(socketId) {
buffer: new Uint8Array(0),
destinationSocketId: null
};
// Track statistics
stats.totalConnections++;
stats.activeConnections++;
stats.connections[socketId] = {
connectTime: Date.now(),
bytesReceived: 0,
bytesSent: 0
};
console.log('[SOCKS] Initialized connection ' + socketId);
sendStatusToWindow();
}
function removeConnection(socketId) {
@@ -90,7 +112,15 @@ function removeConnection(socketId) {
} catch(e) {}
}
delete connections[socketId];
// Update statistics
if (stats.connections[socketId]) {
delete stats.connections[socketId];
}
stats.activeConnections--;
console.log('[SOCKS] Removed connection ' + socketId);
sendStatusToWindow();
}
function handleSocksData(clientSocketId, data) {
@@ -102,11 +132,25 @@ function handleSocksData(clientSocketId, data) {
// If we're in tunnel mode, forward data to destination
if (conn.state === ConnectionState.TUNNEL && conn.destinationSocketId) {
console.log('[Tunnel] Client -> Destination: ' + data.byteLength + ' bytes');
var byteLength = data.byteLength;
console.log('[Tunnel] Client -> Destination: ' + byteLength + ' bytes');
// Track statistics
stats.bytesReceived += byteLength;
if (stats.connections[clientSocketId]) {
stats.connections[clientSocketId].bytesReceived += byteLength;
}
chrome.sockets.tcp.send(conn.destinationSocketId, data, function(sendInfo) {
if (sendInfo.resultCode < 0) {
console.error('[Tunnel] Failed to send to destination: ' + sendInfo.resultCode);
closeConnection(clientSocketId);
return;
}
// Track sent bytes
stats.bytesSent += byteLength;
if (stats.connections[clientSocketId]) {
stats.connections[clientSocketId].bytesSent += byteLength;
}
});
return;
@@ -343,7 +387,14 @@ function handleDestinationData(destSocketId, data) {
return;
}
console.log('[Tunnel] Destination -> Client: ' + data.byteLength + ' bytes');
var byteLength = data.byteLength;
console.log('[Tunnel] Destination -> Client: ' + byteLength + ' bytes');
// Track statistics
stats.bytesReceived += byteLength;
if (stats.connections[clientSocketId]) {
stats.connections[clientSocketId].bytesReceived += byteLength;
}
// Pause destination to prevent event spam while sending
chrome.sockets.tcp.setPaused(destSocketId, true);
@@ -356,6 +407,12 @@ function handleDestinationData(destSocketId, data) {
console.error('[Tunnel] Failed to send to client: ' + sendInfo.resultCode);
// Close both connections
closeConnection(clientSocketId);
return;
}
// Track sent bytes
stats.bytesSent += byteLength;
if (stats.connections[clientSocketId]) {
stats.connections[clientSocketId].bytesSent += byteLength;
}
});
}
@@ -462,8 +519,51 @@ function initializeProxy() {
chrome.app.runtime.onLaunched.addListener(function() {
console.log('[Hammer] App launched');
initializeProxy();
createAppWindow();
});
// Create a minimal app window to prevent Chrome from suspending the app
function createAppWindow() {
// Only create if we don't already have one
if (appWindow) {
try {
appWindow.focus();
return;
} catch(e) {
appWindow = null;
}
}
chrome.app.window.create('window.html', {
'outerBounds': {
'width': 400,
'height': 500,
'minWidth': 300,
'minHeight': 400
},
'resizable': true,
'alwaysOnTop': false
}, function(createdWindow) {
if (chrome.runtime.lastError) {
console.error('[Hammer] Failed to create window:', chrome.runtime.lastError);
return;
}
appWindow = createdWindow;
console.log('[Hammer] App window created - prevents suspension');
// Re-create window if closed
appWindow.onClosed.addListener(function() {
console.log('[Hammer] Window closed, recreating in 2 seconds...');
appWindow = null;
setTimeout(function() {
if (!appWindow) {
createAppWindow();
}
}, 2000);
});
});
}
// Chrome App lifecycle - restart when app is restarted
chrome.app.runtime.onRestarted.addListener(function() {
console.log('[Hammer] App restarted');
@@ -500,6 +600,13 @@ chrome.app.window.onClosed.addListener(function() {
// Also initialize on startup (for apps that are already running)
initializeProxy();
// Create window on startup to prevent suspension
setTimeout(function() {
if (!appWindow) {
createAppWindow();
}
}, 1000);
// ============================================================================
// KEEP-ALIVE MECHANISM
// ============================================================================
@@ -512,6 +619,9 @@ chrome.alarms.onAlarm.addListener(function(alarm) {
// Heartbeat: log activity to keep the app alive
console.log('[Hammer] Heartbeat - Proxy is ' + (isListening ? 'active' : 'inactive'));
// Send status to window
sendStatusToWindow();
// Check if server is still listening, restart if needed
if (!isListening && !isInitialized) {
console.log('[Hammer] Server not listening, reinitializing...');
@@ -523,6 +633,64 @@ chrome.alarms.onAlarm.addListener(function(alarm) {
}
});
// Send status to app window
function sendStatusToWindow() {
// Format bytes for display
function formatBytes(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
}
// Format duration for display
function formatDuration(ms) {
var seconds = Math.floor(ms / 1000);
var minutes = Math.floor(seconds / 60);
var hours = Math.floor(minutes / 60);
if (hours > 0) return hours + 'h ' + (minutes % 60) + 'm';
if (minutes > 0) return minutes + 'm ' + (seconds % 60) + 's';
return seconds + 's';
}
// Calculate connection durations
var connectionDurations = [];
for (var socketId in stats.connections) {
var conn = stats.connections[socketId];
var duration = Date.now() - conn.connectTime;
connectionDurations.push(formatDuration(duration));
}
chrome.runtime.sendMessage({
type: 'status',
active: isListening,
stats: {
totalConnections: stats.totalConnections,
activeConnections: stats.activeConnections,
bytesReceived: formatBytes(stats.bytesReceived),
bytesSent: formatBytes(stats.bytesSent),
uptime: formatDuration(Date.now() - stats.startTime),
connectionDurations: connectionDurations
}
});
chrome.runtime.sendMessage({
type: 'heartbeat'
});
}
// Listen for status requests from window
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.type === 'getStatus') {
sendResponse({
type: 'status',
active: isListening,
stats: {
totalConnections: stats.totalConnections,
activeConnections: stats.activeConnections
}
});
}
});
// Handle when app is suspended and resumed
chrome.runtime.onSuspend.addListener(function() {
console.log('[Hammer] App being suspended...');