feat: Critical SSL connection stability improvements (v1.4.8)

- Enhanced SSL error reporting with detailed error codes and descriptions
- Added connection state validation before SSL operations
- Implemented automatic retry logic for transient SSL errors (SSL_ERROR_WANT_READ/WRITE)
- Added 5-second timeout protection for SSL read operations to prevent indefinite hangs
- Improved WebSocket frame transmission with retry mechanisms and partial write handling
- Applied improvements to all three C tools (wssshc, wsssh, wsscp)
- Updated CHANGELOG.md and TODO.md for version 1.4.8
- Fixed WebSocket frame sending failures that were causing connection drops
- Enhanced connection resilience with better error recovery

Technical Details:
- SSL error diagnostics using SSL_get_error() and ERR_error_string_n()
- Connection state validation using SSL_get_shutdown()
- Timeout protection using select() with 5-second timeout
- Retry logic with configurable limits (up to 3 attempts)
- Consistent error reporting across all SSL operations
- Backward compatible improvements that don't affect normal operation
parent 96207bd8
......@@ -5,6 +5,29 @@ 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.8] - 2025-09-17
### Fixed
- **Critical SSL Connection Stability Issues**: Comprehensive SSL error handling and connection resilience improvements
- Fixed WebSocket frame sending failures that caused connection drops
- Added detailed SSL error reporting with specific error codes and descriptions
- Implemented automatic retry logic for transient SSL errors (SSL_ERROR_WANT_READ/WRITE)
- Added 5-second timeout protection for SSL read operations to prevent indefinite hangs
- Enhanced connection state validation before SSL operations
- Improved WebSocket frame transmission with retry mechanisms and partial write handling
### Technical Details
- **SSL Error Diagnostics**: Enhanced error reporting using `SSL_get_error()` and `ERR_error_string_n()`
- **Connection Resilience**: Automatic retry for temporary network issues with configurable limits
- **Timeout Protection**: Non-blocking SSL operations with proper timeout handling
- **WebSocket Frame Reliability**: Robust frame transmission with error recovery and completion checking
- **Cross-Tool Consistency**: Applied improvements to all three C tools (wssshc, wsssh, wsscp)
### Changed
- **Error Output Format**: Standardized SSL error messages across all tools for better debugging
- **Connection Handling**: Improved resilience to network interruptions and temporary disconnections
- **Debug Information**: Enhanced diagnostic output with specific error codes and retry information
## [1.4.7] - 2025-09-16
### Fixed
......
# WebSocket SSH - Future Enhancements Roadmap
## Recently Completed (v1.4.8)
- [x] **Critical SSL Connection Stability Issues**: Comprehensive SSL error handling and connection resilience improvements
- Fixed WebSocket frame sending failures that caused connection drops
- Added detailed SSL error reporting with specific error codes and descriptions
- Implemented automatic retry logic for transient SSL errors (SSL_ERROR_WANT_READ/WRITE)
- Added 5-second timeout protection for SSL read operations to prevent indefinite hangs
- Enhanced connection state validation before SSL operations
- Improved WebSocket frame transmission with retry mechanisms and partial write handling
- Applied improvements to all three C tools (wssshc, wsssh, wsscp)
- [x] **Documentation Updates**: Updated CHANGELOG.md, README.md, DOCUMENTATION.md, and TODO.md for version 1.4.8
## 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
......
This diff is collapsed.
......@@ -139,7 +139,45 @@ int send_websocket_frame(SSL *ssl, const char *data) {
}
int frame_len = header_len + msg_len;
return SSL_write(ssl, frame, frame_len) > 0;
// Handle partial writes for large frames
int total_written = 0;
int retry_count = 0;
const int max_retries = 3;
while (total_written < frame_len && retry_count < max_retries) {
int to_write = frame_len - total_written;
// Limit to BUFFER_SIZE to avoid issues with very large frames
if (to_write > BUFFER_SIZE) {
to_write = BUFFER_SIZE;
}
int written = SSL_write(ssl, frame + total_written, to_write);
if (written <= 0) {
int ssl_error = SSL_get_error(ssl, written);
// Handle transient SSL errors with retry
if ((ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) && retry_count < max_retries - 1) {
retry_count++;
usleep(10000); // Wait 10ms before retry
continue; // Retry the write operation
}
fprintf(stderr, "WebSocket frame SSL_write failed: %d (after %d retries)\n", ssl_error, retry_count);
char error_buf[256];
ERR_error_string_n(ssl_error, error_buf, sizeof(error_buf));
fprintf(stderr, "SSL write error details: %s\n", error_buf);
return 0; // Write failed
}
total_written += written;
retry_count = 0; // Reset retry count on successful write
}
if (total_written < frame_len) {
fprintf(stderr, "WebSocket frame write incomplete: %d/%d bytes written\n", total_written, frame_len);
return 0;
}
return 1;
}
int send_pong_frame(SSL *ssl, const char *ping_payload, int payload_len) {
......@@ -181,7 +219,45 @@ int send_pong_frame(SSL *ssl, const char *ping_payload, int payload_len) {
}
int frame_len = header_len + payload_len;
return SSL_write(ssl, frame, frame_len) > 0;
// Handle partial writes for large frames
int total_written = 0;
int retry_count = 0;
const int max_retries = 3;
while (total_written < frame_len && retry_count < max_retries) {
int to_write = frame_len - total_written;
// Limit to BUFFER_SIZE to avoid issues with very large frames
if (to_write > BUFFER_SIZE) {
to_write = BUFFER_SIZE;
}
int written = SSL_write(ssl, frame + total_written, to_write);
if (written <= 0) {
int ssl_error = SSL_get_error(ssl, written);
// Handle transient SSL errors with retry
if ((ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) && retry_count < max_retries - 1) {
retry_count++;
usleep(10000); // Wait 10ms before retry
continue; // Retry the write operation
}
fprintf(stderr, "Pong frame SSL_write failed: %d (after %d retries)\n", ssl_error, retry_count);
char error_buf[256];
ERR_error_string_n(ssl_error, error_buf, sizeof(error_buf));
fprintf(stderr, "SSL write error details: %s\n", error_buf);
return 0; // Write failed
}
total_written += written;
retry_count = 0; // Reset retry count on successful write
}
if (total_written < frame_len) {
fprintf(stderr, "Pong frame write incomplete: %d/%d bytes written\n", total_written, frame_len);
return 0;
}
return 1;
}
int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, int *payload_len) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -45,6 +45,7 @@ typedef struct {
int wssshd_port; // wssshd server port
int debug;
int interval; // Reconnection interval in seconds
int dev_tunnel; // Development mode - don't launch SSH, just setup tunnel
} wsssh_config_t;
typedef struct {
......@@ -52,6 +53,7 @@ typedef struct {
int wssshd_port; // wssshd server port
int debug;
int interval; // Connection retry interval in seconds
int dev_tunnel; // Development mode - don't launch SCP, just setup tunnel
} wsscp_config_t;
// Thread arguments
......
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