Add extensive debug logging for control channel messages

- wssshd: Log full content of all received and sent control messages when --debug is enabled
- wssshc: Already logs control messages, registration message logging enhanced
- wsssht: Fixed memory corruption issue with config struct allocations

Control channel messages include: register, registered, registration_error,
tunnel_request, tunnel_ack, tunnel_error, tunnel_close, tunnel_data, tunnel_response
parent 2ea94e22
...@@ -82,12 +82,12 @@ async def handle_websocket(websocket, path=None, *, server_password=None): ...@@ -82,12 +82,12 @@ async def handle_websocket(websocket, path=None, *, server_password=None):
break break
# Process the message (rest of the original logic) # Process the message (rest of the original logic)
# Only log debug info for non-data messages to reduce overhead # Log debug info for all control channel messages when debug is enabled
try: try:
data = json.loads(message) data = json.loads(message)
msg_type = data.get('type', 'unknown') msg_type = data.get('type', 'unknown')
if debug and msg_type not in ('tunnel_data', 'tunnel_response'): if debug:
print(f"[DEBUG] [WebSocket] {msg_type} message received") print(f"[DEBUG] [WebSocket] Received {msg_type} message: {message}")
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
if debug: print(f"[DEBUG] [WebSocket] Invalid JSON received: {e}") if debug: print(f"[DEBUG] [WebSocket] Invalid JSON received: {e}")
continue continue
...@@ -129,13 +129,17 @@ async def handle_websocket(websocket, path=None, *, server_password=None): ...@@ -129,13 +129,17 @@ async def handle_websocket(websocket, path=None, *, server_password=None):
else: else:
print(f"Client {client_id} registered") print(f"Client {client_id} registered")
try: try:
await websocket.send(REGISTERED_MSG % client_id) response_msg = REGISTERED_MSG % client_id
if debug: print(f"[DEBUG] [WebSocket] Sending registration response: {response_msg}")
await websocket.send(response_msg)
except Exception: except Exception:
if debug: print(f"[DEBUG] [WebSocket] Failed to send registration response to {client_id}") if debug: print(f"[DEBUG] [WebSocket] Failed to send registration response to {client_id}")
else: else:
if debug: print(f"[DEBUG] [WebSocket] Client {client_id} registration failed: invalid password") if debug: print(f"[DEBUG] [WebSocket] Client {client_id} registration failed: invalid password")
try: try:
await websocket.send(REGISTRATION_ERROR_MSG % "Invalid password") error_msg = REGISTRATION_ERROR_MSG % "Invalid password"
if debug: print(f"[DEBUG] [WebSocket] Sending registration error: {error_msg}")
await websocket.send(error_msg)
except Exception: except Exception:
if debug: print(f"[DEBUG] [WebSocket] Failed to send registration error to {client_id}") if debug: print(f"[DEBUG] [WebSocket] Failed to send registration error to {client_id}")
elif data.get('type') == 'tunnel_request': elif data.get('type') == 'tunnel_request':
...@@ -177,8 +181,13 @@ async def handle_websocket(websocket, path=None, *, server_password=None): ...@@ -177,8 +181,13 @@ async def handle_websocket(websocket, path=None, *, server_password=None):
# Forward tunnel request to client # Forward tunnel request to client
try: try:
await client_info['websocket'].send(TUNNEL_REQUEST_MSG % request_id) request_msg = TUNNEL_REQUEST_MSG % request_id
await websocket.send(TUNNEL_ACK_MSG % request_id) ack_msg = TUNNEL_ACK_MSG % request_id
if debug:
print(f"[DEBUG] [WebSocket] Sending tunnel request to client: {request_msg}")
print(f"[DEBUG] [WebSocket] Sending tunnel ack to wsssh: {ack_msg}")
await client_info['websocket'].send(request_msg)
await websocket.send(ack_msg)
if not debug: if not debug:
print(f"[EVENT] New tunnel {request_id} for client {client_id}") print(f"[EVENT] New tunnel {request_id} for client {client_id}")
else: else:
...@@ -187,12 +196,16 @@ async def handle_websocket(websocket, path=None, *, server_password=None): ...@@ -187,12 +196,16 @@ async def handle_websocket(websocket, path=None, *, server_password=None):
tunnel.update_status(TunnelStatus.ERROR, str(e)) tunnel.update_status(TunnelStatus.ERROR, str(e))
# Send error response for tunnel request failures # Send error response for tunnel request failures
try: try:
await websocket.send(TUNNEL_ERROR_MSG % (request_id, "Failed to forward request")) error_msg = TUNNEL_ERROR_MSG % (request_id, "Failed to forward request")
if debug: print(f"[DEBUG] [WebSocket] Sending tunnel error: {error_msg}")
await websocket.send(error_msg)
except Exception: except Exception:
pass # Silent failure if even error response fails pass # Silent failure if even error response fails
else: else:
try: try:
await websocket.send(TUNNEL_ERROR_MSG % (request_id, "Client not registered or disconnected")) error_msg = TUNNEL_ERROR_MSG % (request_id, "Client not registered or disconnected")
if debug: print(f"[DEBUG] [WebSocket] Sending tunnel error: {error_msg}")
await websocket.send(error_msg)
except Exception: except Exception:
pass # Silent failure for error responses pass # Silent failure for error responses
elif data.get('type') == 'tunnel_data': elif data.get('type') == 'tunnel_data':
...@@ -232,7 +245,9 @@ async def handle_websocket(websocket, path=None, *, server_password=None): ...@@ -232,7 +245,9 @@ async def handle_websocket(websocket, path=None, *, server_password=None):
client_info = clients.get(tunnel.client_id) client_info = clients.get(tunnel.client_id)
if client_info and client_info['status'] == 'active': if client_info and client_info['status'] == 'active':
try: try:
await tunnel.client_ws.send(TUNNEL_CLOSE_MSG % request_id) close_msg = TUNNEL_CLOSE_MSG % request_id
if debug: print(f"[DEBUG] [WebSocket] Sending tunnel close to client: {close_msg}")
await tunnel.client_ws.send(close_msg)
except Exception: except Exception:
# Silent failure for performance # Silent failure for performance
pass pass
......
...@@ -275,75 +275,33 @@ int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_ar ...@@ -275,75 +275,33 @@ int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_ar
int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *wssshd_host, int wssshd_port) { int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *wssshd_host, int wssshd_port) {
// Bridge mode: JSON messages to/from stdin/stdout // Bridge mode: Pure transport layer - no tunnel setup, just WebSocket transport
if (config->debug) { if (config->debug) {
printf("[DEBUG] Starting bridge mode with client_id=%s, host=%s, port=%d\n", printf("[DEBUG] Starting bridge mode (pure transport) with client_id=%s, host=%s, port=%d\n",
client_id, wssshd_host, wssshd_port); client_id, wssshd_host, wssshd_port);
fflush(stdout); fflush(stdout);
} }
// Send initial status // Send initial status
printf("{\"type\":\"status\",\"message\":\"Bridge mode started\",\"client_id\":\"%s\",\"host\":\"%s\",\"port\":%d}\n", printf("{\"type\":\"bridge_started\",\"client_id\":\"%s\",\"host\":\"%s\",\"port\":%d,\"timestamp\":%ld}\n",
client_id, wssshd_host, wssshd_port); client_id, wssshd_host, wssshd_port, time(NULL));
fflush(stdout); fflush(stdout);
// Establish tunnel // Establish WebSocket connection (no tunnel setup)
int listen_sock = setup_tunnel(wssshd_host, wssshd_port, client_id, config->local_port ? atoi(config->local_port) : find_available_port(), SSL_CTX *ws_ctx = NULL;
config->debug, 0, config->tunnel_host); int ws_sock = setup_websocket_connection(wssshd_host, wssshd_port, client_id, config->debug, &ws_ctx);
if (ws_sock < 0) {
if (listen_sock < 0) { printf("{\"type\":\"error\",\"message\":\"Failed to establish WebSocket connection\",\"timestamp\":%ld}\n", time(NULL));
printf("{\"type\":\"error\",\"message\":\"Failed to establish tunnel\"}\n");
fflush(stdout); fflush(stdout);
if (ws_ctx) SSL_CTX_free(ws_ctx);
return 1; return 1;
} }
// Send tunnel established message // Send connection established message
printf("{\"type\":\"tunnel_established\",\"listen_sock\":%d}\n", listen_sock); printf("{\"type\":\"websocket_connected\",\"socket\":%d,\"timestamp\":%ld}\n", ws_sock, time(NULL));
fflush(stdout);
// Wait for connection
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int accepted_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (accepted_sock < 0) {
printf("{\"type\":\"error\",\"message\":\"Failed to accept connection\"}\n");
fflush(stdout);
close(listen_sock);
return 1;
}
close(listen_sock);
// Send connection accepted message
printf("{\"type\":\"connection_accepted\",\"socket\":%d}\n", accepted_sock);
fflush(stdout); fflush(stdout);
// Set up tunnel for accepted connection // Main bridge loop - pure transport layer
pthread_mutex_lock(&tunnel_mutex);
active_tunnel->local_sock = accepted_sock;
// Send any buffered data
if (active_tunnel->incoming_buffer && active_tunnel->incoming_buffer->used > 0) {
printf("{\"type\":\"buffered_data\",\"bytes\":%zu}\n", active_tunnel->incoming_buffer->used);
fflush(stdout);
}
pthread_mutex_unlock(&tunnel_mutex);
// Start forwarding thread
thread_args_t *thread_args = malloc(sizeof(thread_args_t));
if (thread_args) {
thread_args->ssl = active_tunnel->ssl;
thread_args->tunnel = active_tunnel;
thread_args->debug = config->debug;
pthread_t thread;
pthread_create(&thread, NULL, forward_tcp_to_ws, thread_args);
pthread_detach(thread);
}
// Main bridge loop - handle stdin/stdout communication and tunnel control messages
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
fd_set readfds; fd_set readfds;
struct timeval tv; struct timeval tv;
...@@ -352,37 +310,27 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -352,37 +310,27 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
static char frame_buffer[BUFFER_SIZE * 4]; static char frame_buffer[BUFFER_SIZE * 4];
static int frame_buffer_used = 0; static int frame_buffer_used = 0;
printf("{\"type\":\"ready\",\"message\":\"Bridge mode active\"}\n"); printf("{\"type\":\"bridge_ready\",\"message\":\"Pure transport layer active\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout); fflush(stdout);
while (1) { while (1) {
// Check for stdin input (JSON commands) // Check for stdin input (messages to send to server)
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds); FD_SET(STDIN_FILENO, &readfds);
FD_SET(ws_sock, &readfds);
// Also check for WebSocket data if tunnel is active
pthread_mutex_lock(&tunnel_mutex);
int ssl_fd = -1;
if (active_tunnel && active_tunnel->ssl) {
ssl_fd = SSL_get_fd(active_tunnel->ssl);
if (ssl_fd >= 0) {
FD_SET(ssl_fd, &readfds);
}
}
pthread_mutex_unlock(&tunnel_mutex);
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 50000; // 50ms timeout tv.tv_usec = 50000; // 50ms timeout
int max_fd = (ssl_fd > STDIN_FILENO) ? ssl_fd : STDIN_FILENO; int max_fd = (ws_sock > STDIN_FILENO) ? ws_sock : STDIN_FILENO;
int retval = select(max_fd + 1, &readfds, NULL, NULL, &tv); int retval = select(max_fd + 1, &readfds, NULL, NULL, &tv);
if (retval > 0) { if (retval > 0) {
// Handle stdin input (JSON commands) // Handle stdin input (messages to send to server)
if (FD_ISSET(STDIN_FILENO, &readfds)) { if (FD_ISSET(STDIN_FILENO, &readfds)) {
if (fgets(buffer, sizeof(buffer), stdin) == NULL) { if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
// EOF or error on stdin // EOF or error on stdin
printf("{\"type\":\"stdin_closed\"}\n"); printf("{\"type\":\"stdin_closed\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout); fflush(stdout);
break; break;
} }
...@@ -391,24 +339,33 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -391,24 +339,33 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
size_t len = strlen(buffer); size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') { if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0'; buffer[len-1] = '\0';
len--;
} }
// Parse and handle JSON command if (len > 0) {
printf("{\"type\":\"command_received\",\"command\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL)); // Determine channel based on message content
fflush(stdout); const char *channel = "control";
if (strstr(buffer, "\"type\":\"tunnel_data\"") || strstr(buffer, "tunnel_data")) {
channel = "data";
}
// For now, just echo back - in full implementation would parse JSON and handle commands // Send message to appropriate WebSocket channel
if (send_websocket_message(ws_sock, buffer, len, channel, config->debug) == 0) {
printf("{\"type\":\"message_sent\",\"channel\":\"%s\",\"message\":\"%s\",\"timestamp\":%ld}\n",
channel, buffer, time(NULL));
fflush(stdout);
} else {
printf("{\"type\":\"send_error\",\"channel\":\"%s\",\"timestamp\":%ld}\n", channel, time(NULL));
fflush(stdout);
}
}
} }
// Handle WebSocket data (tunnel control messages) // Handle WebSocket data (messages from server)
if (ssl_fd >= 0 && FD_ISSET(ssl_fd, &readfds)) { if (FD_ISSET(ws_sock, &readfds)) {
pthread_mutex_lock(&tunnel_mutex); if ((size_t)frame_buffer_used < sizeof(frame_buffer)) {
SSL *current_ssl = active_tunnel ? active_tunnel->ssl : NULL; int bytes_read = recv(ws_sock, frame_buffer + frame_buffer_used,
pthread_mutex_unlock(&tunnel_mutex); sizeof(frame_buffer) - frame_buffer_used, 0);
if (current_ssl && (size_t)frame_buffer_used < sizeof(frame_buffer)) {
int bytes_read = SSL_read(current_ssl, frame_buffer + frame_buffer_used,
sizeof(frame_buffer) - frame_buffer_used);
if (bytes_read > 0) { if (bytes_read > 0) {
frame_buffer_used += bytes_read; frame_buffer_used += bytes_read;
...@@ -431,25 +388,28 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -431,25 +388,28 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
memcpy(buffer, payload, payload_len); memcpy(buffer, payload, payload_len);
buffer[payload_len] = '\0'; buffer[payload_len] = '\0';
// Handle tunnel control messages // Determine channel and forward message
if (strstr(buffer, "tunnel_data")) { const char *channel = "control";
printf("{\"type\":\"tunnel_data\",\"message\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL)); if (strstr(buffer, "\"type\":\"tunnel_data\"") || strstr(buffer, "tunnel_data")) {
fflush(stdout); channel = "data";
} else if (strstr(buffer, "tunnel_response")) {
printf("{\"type\":\"tunnel_response\",\"message\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL));
fflush(stdout);
} else if (strstr(buffer, "tunnel_close")) {
printf("{\"type\":\"tunnel_close\",\"message\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL));
fflush(stdout);
} else {
// Other WebSocket messages
printf("{\"type\":\"websocket_message\",\"message\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL));
fflush(stdout);
} }
printf("{\"type\":\"message_received\",\"channel\":\"%s\",\"message\":\"%s\",\"timestamp\":%ld}\n",
channel, buffer, time(NULL));
fflush(stdout);
} }
} else if (frame_type == 0x88) { // Close frame } else if (frame_type == 0x88) { // Close frame
printf("{\"type\":\"websocket_close\",\"timestamp\":%ld}\n", time(NULL)); printf("{\"type\":\"websocket_close\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout); fflush(stdout);
break;
} else if (frame_type == 0x89) { // Ping frame
printf("{\"type\":\"ping_received\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
// Send pong
send_pong_frame_ws(ws_sock, payload, payload_len);
} else if (frame_type == 0x8A) { // Pong frame
printf("{\"type\":\"pong_received\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
} }
// Remove processed frame from buffer // Remove processed frame from buffer
...@@ -466,33 +426,34 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -466,33 +426,34 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
printf("{\"type\":\"websocket_connection_closed\",\"timestamp\":%ld}\n", time(NULL)); printf("{\"type\":\"websocket_connection_closed\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout); fflush(stdout);
break; break;
} else {
// Error
printf("{\"type\":\"websocket_error\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
break;
} }
} }
} }
} }
// Check tunnel status
pthread_mutex_lock(&tunnel_mutex);
int tunnel_active = active_tunnel && active_tunnel->active;
int tunnel_broken = active_tunnel && active_tunnel->broken;
pthread_mutex_unlock(&tunnel_mutex);
if (!tunnel_active) {
if (tunnel_broken) {
printf("{\"type\":\"tunnel_broken\",\"timestamp\":%ld}\n", time(NULL));
} else {
printf("{\"type\":\"tunnel_closed\",\"timestamp\":%ld}\n", time(NULL));
}
fflush(stdout);
break;
}
// Small delay to prevent busy looping // Small delay to prevent busy looping
usleep(25000); // 25ms usleep(25000); // 25ms
} }
// Cleanup // Cleanup
printf("{\"type\":\"bridge_ended\"}\n"); close(ws_sock);
// Clean up SSL resources for bridge mode
if (active_tunnel && active_tunnel->ssl) {
SSL_free(active_tunnel->ssl);
active_tunnel->ssl = NULL;
}
if (ws_ctx) {
SSL_CTX_free(ws_ctx);
ws_ctx = NULL;
}
printf("{\"type\":\"bridge_ended\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout); fflush(stdout);
return 0; return 0;
...@@ -1243,10 +1204,11 @@ cleanup_and_exit: ...@@ -1243,10 +1204,11 @@ cleanup_and_exit:
free(config->tunnel); free(config->tunnel);
free(config->tunnel_control); free(config->tunnel_control);
free(config->service); free(config->service);
free(config->local_port); // Set to NULL to prevent double-free in main()
free(config->tunnel); config->local_port = NULL;
free(config->tunnel_control); config->tunnel = NULL;
free(config->service); config->tunnel_control = NULL;
config->service = NULL;
// Note: config_domain, config_clientid, etc. are freed in main() // Note: config_domain, config_clientid, etc. are freed in main()
pthread_mutex_destroy(&tunnel_mutex); pthread_mutex_destroy(&tunnel_mutex);
pthread_mutex_destroy(&ssl_mutex); pthread_mutex_destroy(&ssl_mutex);
...@@ -1296,16 +1258,16 @@ int main(int argc, char *argv[]) { ...@@ -1296,16 +1258,16 @@ int main(int argc, char *argv[]) {
wsssh_config_t config = { wsssh_config_t config = {
.local_port = NULL, .local_port = NULL,
.tunnel_host = config_tunnel_host, .tunnel_host = config_tunnel_host ? strdup(config_tunnel_host) : NULL,
.wssshd_host = config_domain, .wssshd_host = config_domain ? strdup(config_domain) : NULL,
.client_id = config_clientid, .client_id = config_clientid ? strdup(config_clientid) : NULL,
.wssshd_port = config_wssshd_port ? atoi(config_wssshd_port) : 9898, .wssshd_port = config_wssshd_port ? atoi(config_wssshd_port) : 9898,
.debug = 0, .debug = 0,
.interval = 5, .interval = 5,
.dev_tunnel = 0, .dev_tunnel = 0,
.tunnel = config_tunnel, .tunnel = config_tunnel ? strdup(config_tunnel) : NULL,
.tunnel_control = config_tunnel_control, .tunnel_control = config_tunnel_control ? strdup(config_tunnel_control) : NULL,
.service = config_service, .service = config_service ? strdup(config_service) : NULL,
.mode = initial_mode, .mode = initial_mode,
.daemon = initial_daemon .daemon = initial_daemon
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment