Fix critical wsssh process hanging issue after SSH client disconnection

- Add 'broken' flag to tunnel_t struct to distinguish between normal closure and broken connections
- Set broken=1 when detecting EBADF/EPIPE/ECONNRESET errors in tunnel operations
- Modify main loop to immediately kill SSH child process and exit when tunnel breaks
- Exit with code 1 for error conditions, code 0 for normal termination
- Update CHANGELOG.md, README.md, and TODO.md for version 1.4.7
- Prevent indefinite hanging of wsssh process after tunnel failures
parent d6ee31e9
......@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.7] - 2025-09-16
### Fixed
- **Critical Process Exit Bug**: Fixed wsssh process hanging after SSH client disconnection
- Added `broken` flag to tunnel structure to distinguish between normal closure and broken connections
- Implemented proper tunnel state tracking for EBADF, EPIPE, and ECONNRESET errors
- Enhanced error handling in `handle_tunnel_data()` for SSH client disconnections
- Fixed main loop to immediately kill SSH child process and exit when tunnel breaks
- Added proper exit code handling: 0 for normal termination, 1 for error conditions
- Prevented indefinite hanging of wsssh process after tunnel failures
### Technical Details
- **Tunnel State Management**: Added `int broken` field to `tunnel_t` structure for connection state tracking
- **Error Detection**: Enhanced detection of broken SSH connections with proper error classification
- **Process Management**: Improved SSH child process termination logic with immediate cleanup
- **Exit Code Standards**: Implemented standard exit codes (0=success, 1=error) for better automation support
- **Memory Management**: Maintained proper cleanup of tunnel resources in all exit paths
## [1.4.6] - 2025-09-16
### Added
......
......@@ -597,7 +597,25 @@ Your support helps us continue developing and maintaining this open-source proje
## Changelog
### Version 1.4.6 (Latest)
### Version 1.4.7 (Latest)
**Critical Bug Fix:**
- **Process Exit Issue Resolution**: Fixed wsssh process hanging after SSH client disconnection
- Added `broken` flag to tunnel structure to distinguish between normal closure and broken connections
- Implemented proper tunnel state tracking for EBADF, EPIPE, and ECONNRESET errors
- Enhanced error handling in `handle_tunnel_data()` for SSH client disconnections
- Fixed main loop to immediately kill SSH child process and exit when tunnel breaks
- Added proper exit code handling: 0 for normal termination, 1 for error conditions
- Prevented indefinite hanging of wsssh process after tunnel failures
**Technical Improvements:**
- **Tunnel State Management**: Added `int broken` field to `tunnel_t` structure for connection state tracking
- **Error Detection**: Enhanced detection of broken SSH connections with proper error classification
- **Process Management**: Improved SSH child process termination logic with immediate cleanup
- **Exit Code Standards**: Implemented standard exit codes (0=success, 1=error) for better automation support
- **Memory Management**: Maintained proper cleanup of tunnel resources in all exit paths
### Version 1.4.6
**Major Refactoring:**
- **Code Architecture Overhaul**: Major refactoring to eliminate code duplication
......
# WebSocket SSH - Future Enhancements Roadmap
## Recently Completed (v1.4.7)
- [x] **Critical Process Exit Bug Fix**: Fixed wsssh process hanging after SSH client disconnection
- Added `broken` flag to tunnel structure to distinguish between normal closure and broken connections
- Implemented proper tunnel state tracking for EBADF, EPIPE, and ECONNRESET errors
- Enhanced error handling in `handle_tunnel_data()` for SSH client disconnections
- Fixed main loop to immediately kill SSH child process and exit when tunnel breaks
- Added proper exit code handling: 0 for normal termination, 1 for error conditions
- Prevented indefinite hanging of wsssh process after tunnel failures
- [x] **Documentation Updates**: Updated CHANGELOG.md, README.md, DOCUMENTATION.md, and TODO.md for version 1.4.7
## Recently Completed (v1.4.6)
- [x] **Code Refactoring and Library Architecture**: Major refactoring to eliminate code duplication
- Created shared libraries: `wssshlib.h/.c`, `websocket.h/.c`, `wssh_ssl.h/.c`, `tunnel.h/.c`
......
......@@ -139,13 +139,15 @@ void handle_tunnel_request(SSL *ssl, const char *request_id, int debug) {
active_tunnel->local_sock = -1; // Not used in wssshc
strcpy(active_tunnel->request_id, request_id);
active_tunnel->active = 1;
active_tunnel->broken = 0;
active_tunnel->ssl = ssl;
active_tunnel->outgoing_buffer = NULL; // wssshc doesn't use buffer
active_tunnel->incoming_buffer = NULL; // wssshc doesn't need incoming buffer
pthread_mutex_unlock(&tunnel_mutex);
if (debug) {
printf("[DEBUG] wssshc connected to target SSH server\n");
printf("[DEBUG - Tunnel] wssshc connected to target SSH server\n");
fflush(stdout);
}
// Send tunnel_ack back to server
......@@ -153,7 +155,8 @@ void handle_tunnel_request(SSL *ssl, const char *request_id, int debug) {
snprintf(ack_msg, sizeof(ack_msg), "{\"type\":\"tunnel_ack\",\"request_id\":\"%s\"}", request_id);
if (debug) {
printf("[DEBUG] Sending tunnel_ack: %s\n", ack_msg);
printf("[DEBUG - WebSockets] Sending tunnel_ack: %s\n", ack_msg);
fflush(stdout);
}
if (!send_websocket_frame(ssl, ack_msg)) {
......@@ -223,7 +226,7 @@ void *forward_tcp_to_ws(void *arg) {
// Check if socket is valid
if (sock < 0) {
if (debug) {
printf("[DEBUG] Socket not ready yet, waiting...\n");
printf("[DEBUG - Tunnel] Socket not ready yet, waiting...\n");
fflush(stdout);
}
pthread_mutex_unlock(&tunnel_mutex);
......@@ -256,7 +259,7 @@ void *forward_tcp_to_ws(void *arg) {
// Send any buffered data to the SSH client
if (active_tunnel->incoming_buffer && active_tunnel->incoming_buffer->used > 0) {
if (debug) {
printf("[DEBUG] Sending %zu bytes of buffered data to SSH client\n", active_tunnel->incoming_buffer->used);
printf("[DEBUG - TCPConnection] Sending %zu bytes of buffered data to SSH client\n", active_tunnel->incoming_buffer->used);
fflush(stdout);
}
ssize_t sent = send(client_sock, active_tunnel->incoming_buffer->buffer, active_tunnel->incoming_buffer->used, 0);
......@@ -270,7 +273,7 @@ void *forward_tcp_to_ws(void *arg) {
}
if (debug) {
printf("[DEBUG] SSH client connected, starting data forwarding\n");
printf("[DEBUG - Tunnel] SSH client connected, starting data forwarding\n");
fflush(stdout);
}
}
......@@ -281,7 +284,7 @@ void *forward_tcp_to_ws(void *arg) {
if (sent > 0) {
frame_buffer_consume(active_tunnel->outgoing_buffer, sent);
if (debug) {
printf("[DEBUG] Sent %zd bytes from buffer to local socket\n", sent);
printf("[DEBUG - TCPConnection] Sent %zd bytes from buffer to local socket\n", sent);
fflush(stdout);
}
} else if (sent == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
......@@ -319,9 +322,9 @@ void *forward_tcp_to_ws(void *arg) {
if (bytes_read <= 0) {
if (debug) {
if (bytes_read == 0) {
printf("[DEBUG] TCP connection closed by peer (SSH client disconnected)\n");
printf("[DEBUG - TCPConnection] TCP connection closed by peer (SSH client disconnected)\n");
} else {
printf("[DEBUG] TCP connection error: %s\n", strerror(errno));
printf("[DEBUG - TCPConnection] TCP connection error: %s\n", strerror(errno));
}
fflush(stdout);
}
......@@ -329,13 +332,20 @@ void *forward_tcp_to_ws(void *arg) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel) {
active_tunnel->active = 0;
active_tunnel->broken = 1;
// Send tunnel_close notification immediately when local connection breaks
if (debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close notification from forwarding thread...\n");
fflush(stdout);
}
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, debug);
}
pthread_mutex_unlock(&tunnel_mutex);
break;
}
if (debug) {
printf("[DEBUG] Forwarding %d bytes from TCP to WebSocket\n", bytes_read);
printf("[DEBUG - TCPConnection] Forwarding %d bytes from TCP to WebSocket\n", bytes_read);
fflush(stdout);
}
......@@ -371,10 +381,25 @@ void *forward_tcp_to_ws(void *arg) {
}
if (debug) {
printf("[DEBUG] TCP to WebSocket forwarding thread exiting\n");
printf("[DEBUG - TCPConnection] TCP to WebSocket forwarding thread exiting\n");
fflush(stdout);
}
// Mark tunnel as inactive when forwarding thread exits due to broken connection
if (active_tunnel) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel->active) {
active_tunnel->active = 0;
if (debug) {
printf("[DEBUG - TCPConnection] Marked tunnel as inactive due to forwarding thread exit\n");
fflush(stdout);
}
// Send tunnel_close notification
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, debug);
}
pthread_mutex_unlock(&tunnel_mutex);
}
free(args);
return NULL;
}
......@@ -422,14 +447,14 @@ void *forward_ws_to_ssh_server(void *arg) {
bytes_read = recv(ssh_sock, buffer, sizeof(buffer), 0);
if (bytes_read <= 0) {
if (debug) {
printf("[DEBUG] SSH server connection closed or error\n");
printf("[DEBUG - TCPConnection] SSH server connection closed or error\n");
fflush(stdout);
}
break;
}
if (debug) {
printf("[DEBUG] Forwarding %d bytes from SSH server to WebSocket\n", bytes_read);
printf("[DEBUG - TCPConnection] Forwarding %d bytes from SSH server to WebSocket\n", bytes_read);
fflush(stdout);
}
......@@ -465,7 +490,7 @@ void *forward_ws_to_ssh_server(void *arg) {
}
if (debug) {
printf("[DEBUG] SSH server to WebSocket forwarding thread exiting\n");
printf("[DEBUG - TCPConnection] SSH server to WebSocket forwarding thread exiting\n");
fflush(stdout);
}
......@@ -595,6 +620,9 @@ void handle_tunnel_data(SSL *ssl __attribute__((unused)), const char *request_id
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel) {
active_tunnel->active = 0;
active_tunnel->broken = 1;
// Send tunnel_close notification immediately
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, debug);
}
pthread_mutex_unlock(&tunnel_mutex);
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
......@@ -613,6 +641,9 @@ void handle_tunnel_data(SSL *ssl __attribute__((unused)), const char *request_id
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel) {
active_tunnel->active = 0;
active_tunnel->broken = 1;
// Send tunnel_close notification immediately
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, debug);
}
pthread_mutex_unlock(&tunnel_mutex);
} else {
......@@ -639,6 +670,23 @@ void handle_tunnel_data(SSL *ssl __attribute__((unused)), const char *request_id
free(data);
}
void send_tunnel_close(SSL *ssl, const char *request_id, int debug) {
char close_msg[256];
snprintf(close_msg, sizeof(close_msg), "{\"type\":\"tunnel_close\",\"request_id\":\"%s\"}", request_id);
if (debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close: %s\n", close_msg);
fflush(stdout);
}
if (!send_websocket_frame(ssl, close_msg)) {
if (debug) {
printf("[DEBUG - Tunnel] Failed to send tunnel_close message\n");
fflush(stdout);
}
}
}
void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_id, int debug) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && strcmp(active_tunnel->request_id, request_id) == 0) {
......@@ -658,7 +706,8 @@ void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_i
free(active_tunnel);
active_tunnel = NULL;
if (debug) {
printf("[DEBUG] Tunnel %s closed\n", request_id);
printf("[DEBUG - Tunnel] Tunnel %s closed\n", request_id);
fflush(stdout);
}
}
pthread_mutex_unlock(&tunnel_mutex);
......@@ -1008,6 +1057,7 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
strcpy(active_tunnel->request_id, request_id);
active_tunnel->local_sock = -1;
active_tunnel->active = 1;
active_tunnel->broken = 0;
active_tunnel->ssl = ssl;
// Start listening on local port
......
......@@ -36,6 +36,7 @@ typedef struct {
int sock; // SSH client connection socket (for wsssh) or SSH server connection socket (for wssshc)
char request_id[37]; // UUID string
int active;
int broken; // Flag to indicate if the tunnel is broken (not normal closure)
SSL *ssl; // WebSocket SSL connection
frame_buffer_t *outgoing_buffer; // Buffer for data to send to local socket (wsscp only)
frame_buffer_t *incoming_buffer; // Buffer for incoming data before connection is established
......@@ -59,6 +60,7 @@ void *tunnel_thread(void *arg);
void handle_tunnel_request(SSL *ssl, const char *request_id, int debug);
void handle_tunnel_data(SSL *ssl, const char *request_id, const char *data_hex, int debug);
void handle_tunnel_close(SSL *ssl, const char *request_id, int debug);
void send_tunnel_close(SSL *ssl, const char *request_id, int debug);
void cleanup_tunnel(int debug);
int reconnect_websocket(tunnel_t *tunnel, const char *wssshd_host, int wssshd_port, const char *client_id, const char *request_id, int debug);
int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id, int local_port, int debug, int use_buffer);
......
......@@ -544,6 +544,15 @@ int main(int argc, char *argv[]) {
break;
}
// Check if tunnel became inactive during processing
if (!active_tunnel || !active_tunnel->active) {
if (config.debug) {
printf("[DEBUG] Tunnel became inactive, exiting main loop\n");
fflush(stdout);
}
break;
}
// Use select to wait for data on SSL socket with timeout
FD_ZERO(&readfds);
FD_SET(ssl_fd, &readfds);
......@@ -558,6 +567,14 @@ int main(int argc, char *argv[]) {
}
break;
} else if (retval == 0) {
// Timeout, check if tunnel became inactive during timeout
if (!active_tunnel || !active_tunnel->active) {
if (config.debug) {
printf("[DEBUG] Tunnel became inactive during timeout, exiting\n");
fflush(stdout);
}
break;
}
// Timeout, continue loop
continue;
}
......@@ -886,5 +903,6 @@ cleanup:
free(config_domain);
pthread_mutex_destroy(&tunnel_mutex);
return 0;
// Ensure we exit the process
exit(0);
}
\ No newline at end of file
......@@ -146,7 +146,8 @@ int parse_ssh_args(int argc, char *argv[], char **host, int debug) {
// First non-option argument should be the host
*host = argv[i];
if (debug) {
printf("[DEBUG] Found SSH host: %s\n", *host);
printf("[DEBUG - Tunnel] Found SSH host: %s\n", *host);
fflush(stdout);
}
break;
}
......@@ -305,10 +306,11 @@ int main(int argc, char *argv[]) {
}
if (config.debug) {
printf("[DEBUG] SSH Host: %s\n", ssh_host);
printf("[DEBUG] Client ID: %s\n", client_id);
printf("[DEBUG] WSSSHD Host: %s\n", wssshd_host);
printf("[DEBUG] WSSSHD Port: %d\n", wssshd_port);
printf("[DEBUG - Tunnel] SSH Host: %s\n", ssh_host);
printf("[DEBUG - Tunnel] Client ID: %s\n", client_id);
printf("[DEBUG - Tunnel] WSSSHD Host: %s\n", wssshd_host);
printf("[DEBUG - Tunnel] WSSSHD Port: %d\n", wssshd_port);
fflush(stdout);
}
// Find available local port
......@@ -323,7 +325,8 @@ int main(int argc, char *argv[]) {
}
if (config.debug) {
printf("[DEBUG] Using local port: %d\n", local_port);
printf("[DEBUG - Tunnel] Using local port: %d\n", local_port);
fflush(stdout);
}
// Modify SSH arguments
......@@ -339,7 +342,7 @@ int main(int argc, char *argv[]) {
}
if (config.debug) {
printf("[DEBUG] Modified SSH command:");
printf("[DEBUG - Tunnel] Modified SSH command:");
for (int i = 0; new_ssh_args[i]; i++) {
printf(" %s", new_ssh_args[i]);
}
......@@ -349,7 +352,7 @@ int main(int argc, char *argv[]) {
// Parent process: setup tunnel FIRST
if (config.debug) {
printf("[DEBUG] Parent process setting up tunnel...\n");
printf("[DEBUG - Tunnel] Parent process setting up tunnel...\n");
fflush(stdout);
}
......@@ -360,7 +363,7 @@ int main(int argc, char *argv[]) {
while (setup_attempts < max_setup_attempts && listen_sock < 0) {
if (config.debug && setup_attempts > 0) {
printf("[DEBUG] Initial tunnel setup attempt %d/%d\n", setup_attempts + 1, max_setup_attempts);
printf("[DEBUG - Tunnel] Initial tunnel setup attempt %d/%d\n", setup_attempts + 1, max_setup_attempts);
fflush(stdout);
}
......@@ -370,7 +373,7 @@ int main(int argc, char *argv[]) {
setup_attempts++;
if (setup_attempts < max_setup_attempts) {
if (config.debug) {
printf("[DEBUG] Initial tunnel setup failed, waiting %d seconds...\n", config.interval);
printf("[DEBUG - Tunnel] Initial tunnel setup failed, waiting %d seconds...\n", config.interval);
fflush(stdout);
}
sleep(config.interval);
......@@ -395,7 +398,7 @@ int main(int argc, char *argv[]) {
// NOW fork to run SSH in background (after tunnel is ready)
if (config.debug) {
printf("[DEBUG] About to fork SSH process...\n");
printf("[DEBUG - Tunnel] About to fork SSH process...\n");
fflush(stdout);
}
pid_t pid = fork();
......@@ -417,7 +420,7 @@ int main(int argc, char *argv[]) {
if (pid == 0) {
// Child process: run SSH
if (config.debug) {
printf("[DEBUG] Child process starting SSH...\n");
printf("[DEBUG - Tunnel] Child process starting SSH...\n");
fflush(stdout);
}
execvp("ssh", new_ssh_args);
......@@ -437,7 +440,7 @@ int main(int argc, char *argv[]) {
// Parent process: accept SSH connection and start forwarding
if (config.debug) {
printf("[DEBUG] Waiting for SSH connection on localhost:%d...\n", local_port);
printf("[DEBUG - Tunnel] Waiting for SSH connection on localhost:%d...\n", local_port);
fflush(stdout);
}
......@@ -472,7 +475,7 @@ int main(int argc, char *argv[]) {
pthread_mutex_unlock(&tunnel_mutex);
if (config.debug) {
printf("[DEBUG] Local SSH connection accepted! Starting data forwarding...\n");
printf("[DEBUG - Tunnel] Local SSH connection accepted! Starting data forwarding...\n");
fflush(stdout);
}
......@@ -519,16 +522,76 @@ int main(int argc, char *argv[]) {
int bytes_read;
fd_set readfds;
struct timeval tv;
int tunnel_broken = 0;
while (1) {
// Get SSL fd with mutex protection
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
if (active_tunnel && active_tunnel->broken) {
tunnel_broken = 1;
kill(pid, SIGTERM);
pthread_mutex_unlock(&tunnel_mutex);
goto cleanup_and_exit;
} else {
// normal closure
pthread_mutex_unlock(&tunnel_mutex);
if (config.debug) {
printf("[DEBUG - Tunnel] Tunnel is no longer active, exiting main loop\n");
fflush(stdout);
}
break;
}
}
int ssl_fd = SSL_get_fd(active_tunnel->ssl);
current_ssl = active_tunnel->ssl;
// Check if local socket is still valid
if (active_tunnel->local_sock < 0) {
if (config.debug) {
printf("[DEBUG - Tunnel] Local socket is invalid, tunnel broken\n");
fflush(stdout);
}
tunnel_broken = 1;
active_tunnel->broken = 1;
// Send tunnel_close notification immediately when local connection breaks
if (config.debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close notification due to invalid local socket...\n");
fflush(stdout);
}
send_tunnel_close(current_ssl, active_tunnel->request_id, config.debug);
pthread_mutex_unlock(&tunnel_mutex);
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
// Also check if the local socket connection is broken by trying to peek at it
char test_buf[1];
int result = recv(active_tunnel->local_sock, test_buf, 1, MSG_PEEK | MSG_DONTWAIT);
if (result == 0 || (result < 0 && (errno == ECONNRESET || errno == EPIPE || errno == EBADF))) {
if (config.debug) {
printf("[DEBUG - Tunnel] Local socket connection is broken (errno=%d), sending tunnel_close\n", errno);
fflush(stdout);
}
tunnel_broken = 1;
active_tunnel->broken = 1;
// Send tunnel_close notification immediately when local connection breaks
send_tunnel_close(current_ssl, active_tunnel->request_id, config.debug);
pthread_mutex_unlock(&tunnel_mutex);
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
pthread_mutex_unlock(&tunnel_mutex);
// Use select to wait for data on SSL socket with timeout
FD_ZERO(&readfds);
......@@ -539,11 +602,42 @@ int main(int argc, char *argv[]) {
int retval = select(ssl_fd + 1, &readfds, NULL, NULL, &tv);
if (retval == -1) {
if (config.debug) {
perror("[DEBUG] select on SSL fd failed");
perror("[DEBUG - WebSockets] select on SSL fd failed");
fflush(stdout);
}
break;
tunnel_broken = 1;
// Send tunnel_close notification before breaking
if (config.debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close notification due to select failure...\n");
fflush(stdout);
}
send_tunnel_close(current_ssl, active_tunnel->request_id, config.debug);
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
} else if (retval == 0) {
// Timeout, check if tunnel became inactive during timeout
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
pthread_mutex_unlock(&tunnel_mutex);
if (config.debug) {
printf("[DEBUG - Tunnel] Tunnel became inactive during timeout, exiting\n");
fflush(stdout);
}
tunnel_broken = 1;
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
pthread_mutex_unlock(&tunnel_mutex);
// Timeout, continue loop
continue;
}
......@@ -552,7 +646,7 @@ int main(int argc, char *argv[]) {
bytes_read = SSL_read(current_ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) {
if (config.debug) {
printf("[DEBUG] WebSocket connection lost, attempting reconnection...\n");
printf("[DEBUG - WebSockets] WebSocket connection lost, attempting reconnection...\n");
fflush(stdout);
}
......@@ -563,7 +657,7 @@ int main(int argc, char *argv[]) {
while (reconnect_attempts < max_reconnect_attempts && !reconnected) {
if (config.debug) {
printf("[DEBUG] WebSocket reconnection attempt %d/%d\n", reconnect_attempts + 1, max_reconnect_attempts);
printf("[DEBUG - WebSockets] WebSocket reconnection attempt %d/%d\n", reconnect_attempts + 1, max_reconnect_attempts);
fflush(stdout);
}
......@@ -575,7 +669,7 @@ int main(int argc, char *argv[]) {
if (reconnect_websocket(active_tunnel, wssshd_host, wssshd_port, client_id, active_tunnel->request_id, config.debug) == 0) {
reconnected = 1;
if (config.debug) {
printf("[DEBUG] WebSocket reconnection successful, continuing tunnel\n");
printf("[DEBUG - WebSockets] WebSocket reconnection successful, continuing tunnel\n");
fflush(stdout);
}
// Update ssl_fd for select
......@@ -588,7 +682,7 @@ int main(int argc, char *argv[]) {
reconnect_attempts++;
if (reconnect_attempts < max_reconnect_attempts) {
if (config.debug) {
printf("[DEBUG] WebSocket reconnection failed, waiting 1 second...\n");
printf("[DEBUG - WebSockets] WebSocket reconnection failed, waiting 1 second...\n");
fflush(stdout);
}
sleep(1); // Use 1 second for WebSocket reconnections
......@@ -598,32 +692,77 @@ int main(int argc, char *argv[]) {
if (!reconnected) {
if (config.debug) {
printf("[DEBUG] All reconnection attempts failed, exiting\n");
printf("[DEBUG - WebSockets] All reconnection attempts failed, exiting\n");
fflush(stdout);
}
break;
tunnel_broken = 1;
// Send tunnel_close notification before breaking
if (config.debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close notification due to connection failure...\n");
fflush(stdout);
}
send_tunnel_close(current_ssl, active_tunnel->request_id, config.debug);
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
// Skip processing this iteration since we just reconnected
continue;
}
// Check if tunnel became inactive during WebSocket processing
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
pthread_mutex_unlock(&tunnel_mutex);
if (config.debug) {
printf("[DEBUG - Tunnel] Tunnel became inactive during WebSocket processing, exiting\n");
fflush(stdout);
}
tunnel_broken = 1;
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
pthread_mutex_unlock(&tunnel_mutex);
// Check frame type
unsigned char frame_type = buffer[0] & 0x8F;
// Handle close frame
if (frame_type == 0x88) {
if (config.debug) {
printf("[DEBUG] Received close frame from server\n");
printf("[DEBUG - WebSockets] Received close frame from server\n");
fflush(stdout);
}
break;
tunnel_broken = 1;
// Send tunnel_close notification before breaking
if (config.debug) {
printf("[DEBUG - Tunnel] Sending tunnel_close notification due to server close frame...\n");
fflush(stdout);
}
send_tunnel_close(current_ssl, active_tunnel->request_id, config.debug);
// Kill SSH process and exit immediately
if (config.debug) {
printf("[DEBUG - Tunnel] Killing SSH process and exiting due to broken tunnel\n");
fflush(stdout);
}
kill(pid, SIGTERM);
goto cleanup_and_exit;
}
// Handle ping frame
if (frame_type == 0x89) {
if (config.debug) {
printf("[DEBUG] Received ping frame, sending pong\n");
printf("[DEBUG - WebSockets] Received ping frame, sending pong\n");
fflush(stdout);
}
// Parse the ping frame to get payload
......@@ -633,7 +772,7 @@ int main(int argc, char *argv[]) {
// Send pong with same payload
if (!send_pong_frame(current_ssl, ping_payload, ping_payload_len)) {
if (config.debug) {
printf("[DEBUG] Failed to send pong frame\n");
printf("[DEBUG - WebSockets] Failed to send pong frame\n");
fflush(stdout);
}
}
......@@ -661,7 +800,7 @@ int main(int argc, char *argv[]) {
int payload_len;
if (!parse_websocket_frame(buffer, bytes_read, &payload, &payload_len)) {
if (config.debug) {
printf("[DEBUG] Failed to parse WebSocket frame\n");
printf("[DEBUG - WebSockets] Failed to parse WebSocket frame\n");
fflush(stdout);
}
continue;
......@@ -670,7 +809,7 @@ int main(int argc, char *argv[]) {
payload[payload_len] = '\0';
if (config.debug) {
printf("[DEBUG] Received: %s\n", payload);
printf("[DEBUG - WebSockets] Received: %s\n", payload);
fflush(stdout);
}
......@@ -678,9 +817,9 @@ int main(int argc, char *argv[]) {
if (strstr(payload, "tunnel_data") || strstr(payload, "tunnel_response")) {
if (config.debug) {
if (strstr(payload, "tunnel_data")) {
printf("[DEBUG] Received tunnel_data message\n");
printf("[DEBUG - Tunnel] Received tunnel_data message\n");
} else {
printf("[DEBUG] Received tunnel_response message\n");
printf("[DEBUG - Tunnel] Received tunnel_response message\n");
}
fflush(stdout);
}
......@@ -714,7 +853,7 @@ int main(int argc, char *argv[]) {
}
} else if (strstr(payload, "tunnel_close")) {
if (config.debug) {
printf("[DEBUG] Received tunnel_close message\n");
printf("[DEBUG - Tunnel] Received tunnel_close message\n");
fflush(stdout);
}
char *id_start = strstr(payload, "\"request_id\"");
......@@ -734,38 +873,100 @@ int main(int argc, char *argv[]) {
}
} else {
if (config.debug) {
printf("[DEBUG] Received unknown message type: %s\n", payload);
printf("[DEBUG - WebSockets] Received unknown message type: %s\n", payload);
fflush(stdout);
}
}
}
}
// If we reach here normally (not via goto), handle normal tunnel closure
if (!tunnel_broken) {
if (config.debug) {
printf("[DEBUG] Tunnel loop ended, waiting for SSH to finish...\n");
printf("[DEBUG - Tunnel] Tunnel loop ended normally, sending tunnel_close notification...\n");
fflush(stdout);
}
// Send tunnel_close notification to server
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && active_tunnel->ssl) {
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, config.debug);
}
pthread_mutex_unlock(&tunnel_mutex);
} else {
if (config.debug) {
printf("[DEBUG - Tunnel] Tunnel was already broken, skipping duplicate tunnel_close notification\n");
fflush(stdout);
}
}
// Wait for SSH to finish (only if we didn't kill it already)
if (!tunnel_broken) {
if (config.debug) {
printf("[DEBUG - Tunnel] Waiting for SSH process to finish normally\n");
fflush(stdout);
}
// Wait for SSH to finish
int status;
waitpid(pid, &status, 0);
if (config.debug) {
printf("[DEBUG] SSH process finished with status: %d\n", status);
printf("[DEBUG - Tunnel] SSH process finished with status: %d\n", status);
fflush(stdout);
}
// Wait a few seconds before exiting cleanly
if (config.debug) {
printf("[DEBUG] Waiting 3 seconds before exit...\n");
printf("[DEBUG - Tunnel] Waiting 3 seconds before exit...\n");
fflush(stdout);
}
sleep(3);
}
cleanup_and_exit:
// Cleanup section - reached either normally or via goto
if (config.debug) {
printf("[DEBUG - Tunnel] Performing cleanup and exiting\n");
fflush(stdout);
}
// If tunnel was broken, wait a moment for SSH process to terminate
if (tunnel_broken) {
if (config.debug) {
printf("[DEBUG - Tunnel] Waiting briefly for SSH process to terminate\n");
fflush(stdout);
}
// Give SSH process a moment to terminate gracefully
usleep(100000); // 100ms
// Check if SSH process is still running
int status;
pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) {
if (config.debug) {
printf("[DEBUG - Tunnel] SSH process still running, sending SIGKILL\n");
fflush(stdout);
}
// SSH process is still running, force kill it
kill(pid, SIGKILL);
// Wait for it to actually terminate
waitpid(pid, &status, 0);
} else if (result > 0) {
if (config.debug) {
printf("[DEBUG - Tunnel] SSH process terminated with status: %d\n", status);
fflush(stdout);
}
}
}
// Cleanup
if (active_tunnel) {
if (active_tunnel->local_sock >= 0) {
close(active_tunnel->local_sock);
}
if (active_tunnel->ssl) {
SSL_free(active_tunnel->ssl);
}
free(active_tunnel);
active_tunnel = NULL;
}
......@@ -787,5 +988,11 @@ int main(int argc, char *argv[]) {
free(config_domain);
pthread_mutex_destroy(&tunnel_mutex);
return 0;
if (config.debug) {
printf("[DEBUG - Tunnel] Cleanup complete, exiting with code %d\n", tunnel_broken ? 1 : 0);
fflush(stdout);
}
// Ensure we exit the process
exit(tunnel_broken ? 1 : 0);
}
\ No newline at end of file
......@@ -371,7 +371,8 @@ int connect_to_server(const wssshc_config_t *config) {
fprintf(stderr, "SSL read error\n");
} else {
if (config->debug) {
printf("[DEBUG] Connection closed by server\n");
printf("[DEBUG - WebSockets] Connection closed by server\n");
fflush(stdout);
}
}
// Clean up tunnel resources before breaking
......@@ -380,7 +381,8 @@ int connect_to_server(const wssshc_config_t *config) {
}
if (config->debug) {
printf("[DEBUG] [WebSocket] Read %d bytes, frame: 0x%02x 0x%02x 0x%02x 0x%02x\n", bytes_read, buffer[0], buffer[1], buffer[2], buffer[3]);
printf("[DEBUG - WebSockets] Read %d bytes, frame: 0x%02x 0x%02x 0x%02x 0x%02x\n", bytes_read, buffer[0], buffer[1], buffer[2], buffer[3]);
fflush(stdout);
}
// Parse WebSocket frame with extended length support
......@@ -433,7 +435,8 @@ int connect_to_server(const wssshc_config_t *config) {
// Handle message
if (config->debug) {
printf("[DEBUG] [WebSocket] Received message: %s\n", buffer);
printf("[DEBUG - WebSockets] Received message: %s\n", buffer);
fflush(stdout);
}
// Parse JSON message
......@@ -450,7 +453,8 @@ int connect_to_server(const wssshc_config_t *config) {
if (close_quote) {
*close_quote = '\0';
if (config->debug) {
printf("[DEBUG] [WebSocket] Received tunnel_request for ID: %s\n", id_start);
printf("[DEBUG - WebSockets] Received tunnel_request for ID: %s\n", id_start);
fflush(stdout);
}
handle_tunnel_request(ssl, id_start, config->debug);
}
......@@ -459,7 +463,8 @@ int connect_to_server(const wssshc_config_t *config) {
}
} else if (strstr(buffer, "tunnel_data")) {
if (config->debug) {
printf("[DEBUG] [WebSocket] Received tunnel_data message\n");
printf("[DEBUG - WebSockets] Received tunnel_data message\n");
fflush(stdout);
}
// Extract request_id and data
char *id_start = strstr(buffer, "\"request_id\"");
......@@ -501,7 +506,8 @@ int connect_to_server(const wssshc_config_t *config) {
if (close_quote) {
*close_quote = '\0';
if (config->debug) {
printf("[DEBUG] [WebSocket] Received tunnel_close for ID: %s\n", id_start);
printf("[DEBUG - WebSockets] Received tunnel_close for ID: %s\n", id_start);
fflush(stdout);
}
handle_tunnel_close(ssl, id_start, config->debug);
}
......@@ -516,14 +522,16 @@ int connect_to_server(const wssshc_config_t *config) {
}
} else if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x88) { // Close frame
if (config->debug) {
printf("[DEBUG] [WebSocket] Received close frame - cleaning up and reconnecting...\n");
printf("[DEBUG - WebSockets] Received close frame - cleaning up and reconnecting...\n");
fflush(stdout);
}
// Clean up tunnel resources before reconnecting
cleanup_tunnel(config->debug);
return 0;
} else if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x89) { // Ping frame
if (config->debug) {
printf("[DEBUG] [WebSocket] Received ping frame\n");
printf("[DEBUG - WebSockets] Received ping frame\n");
fflush(stdout);
}
// Parse ping frame and send pong with echoed payload
int masked = buffer[1] & 0x80;
......@@ -620,7 +628,8 @@ int connect_to_server(const wssshc_config_t *config) {
int pong_frame_len = pong_header_len + (int)pong_payload_len;
if (config->debug) {
printf("[DEBUG] [WebSocket] Sending pong frame, len: %d\n", pong_frame_len);
printf("[DEBUG - WebSockets] Sending pong frame, len: %d\n", pong_frame_len);
fflush(stdout);
}
if (SSL_write(ssl, pong_frame, pong_frame_len) <= 0) {
fprintf(stderr, "[ERROR] [WebSocket] Send pong failed\n");
......@@ -681,7 +690,8 @@ int main(int argc, char *argv[]) {
} else if (result == 0) {
// Close frame received - add small delay to prevent rapid reconnection
if (config.debug) {
printf("[DEBUG] Server initiated disconnect, reconnecting in 2 seconds...\n");
printf("[DEBUG - WebSockets] Server initiated disconnect, reconnecting in 2 seconds...\n");
fflush(stdout);
}
sleep(2);
}
......
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