Add tunnel control channel message handling to bridge mode

- Enhanced bridge mode to handle incoming tunnel control messages from WebSocket
- Added JSON output for tunnel_data, tunnel_response, tunnel_close messages
- Added WebSocket connection status messages (websocket_close, websocket_connection_closed)
- Updated test scripts to include tunnel control channel message testing
- Updated documentation with comprehensive examples of control channel messages
- Added new testing scenario for tunnel control channel verification

Bridge mode now properly forwards all tunnel control channel messages as JSON
through stdout, allowing external applications to monitor tunnel state and
server communications in real-time.
parent 549ffa56
...@@ -94,6 +94,7 @@ An interactive testing tool that allows manual JSON command input and real-time ...@@ -94,6 +94,7 @@ An interactive testing tool that allows manual JSON command input and real-time
``` ```
**Response:** **Response:**
```json ```json
{"type":"command_received","command":"{\"command\":\"quit\",\"timestamp\":1640995200}","timestamp":1640995200}
{"type":"bridge_ended","timestamp":1640995200} {"type":"bridge_ended","timestamp":1640995200}
``` ```
...@@ -103,7 +104,36 @@ An interactive testing tool that allows manual JSON command input and real-time ...@@ -103,7 +104,36 @@ An interactive testing tool that allows manual JSON command input and real-time
``` ```
**Response:** **Response:**
```json ```json
{"type":"pong","timestamp":1640995200} {"type":"command_received","command":"{\"command\":\"ping\",\"timestamp\":1640995200}","timestamp":1640995200}
```
### Tunnel Control Channel Messages
These are messages received from the wssshd server and forwarded as JSON through stdout:
#### Tunnel Data Message
```json
{"type":"tunnel_data","message":"{\"request_id\":\"abc123\",\"data\":\"server_response_data\"}","timestamp":1640995200}
```
#### Tunnel Response Message
```json
{"type":"tunnel_response","message":"{\"request_id\":\"abc123\",\"status\":\"success\"}","timestamp":1640995200}
```
#### Tunnel Close Message
```json
{"type":"tunnel_close","message":"{\"request_id\":\"abc123\",\"reason\":\"connection_closed\"}","timestamp":1640995200}
```
#### WebSocket Close Message
```json
{"type":"websocket_close","timestamp":1640995200}
```
#### WebSocket Connection Closed
```json
{"type":"websocket_connection_closed","timestamp":1640995200}
``` ```
### Advanced Commands ### Advanced Commands
...@@ -112,11 +142,19 @@ An interactive testing tool that allows manual JSON command input and real-time ...@@ -112,11 +142,19 @@ An interactive testing tool that allows manual JSON command input and real-time
```json ```json
{"command":"custom","timestamp":1640995200,"data":"additional_info"} {"command":"custom","timestamp":1640995200,"data":"additional_info"}
``` ```
**Response:**
```json
{"type":"command_received","command":"{\"command\":\"custom\",\"timestamp\":1640995200,\"data\":\"additional_info\"}","timestamp":1640995200}
```
#### Help Request #### Help Request
```json ```json
{"command":"help","timestamp":1640995200} {"command":"help","timestamp":1640995200}
``` ```
**Response:**
```json
{"type":"command_received","command":"{\"command\":\"help\",\"timestamp\":1640995200}","timestamp":1640995200}
```
## Bridge Mode Workflow ## Bridge Mode Workflow
...@@ -191,7 +229,22 @@ done < /proc/$WSSSHT_PID/fd/1 ...@@ -191,7 +229,22 @@ done < /proc/$WSSSHT_PID/fd/1
kill $WSSSHT_PID kill $WSSSHT_PID
``` ```
### Scenario 4: Error Handling ### Scenario 4: Tunnel Control Channel Testing
```bash
# Test tunnel control channel messages
./test_bridge_interactive.sh start
# In interactive mode, you can observe:
# - tunnel_data messages when server sends data
# - tunnel_response messages for server responses
# - tunnel_close messages when tunnel closes
# - websocket_close messages for WebSocket events
# Example: Monitor for 30 seconds to see control messages
timeout 30 ./test_bridge_interactive.sh monitor
```
### Scenario 5: Error Handling
```bash ```bash
# Test with invalid client ID # Test with invalid client ID
./test_bridge_mode.sh --client-id invalid_client ./test_bridge_mode.sh --client-id invalid_client
......
...@@ -68,6 +68,19 @@ Available JSON Commands for Bridge Mode: ...@@ -68,6 +68,19 @@ Available JSON Commands for Bridge Mode:
5. Help Command: 5. Help Command:
{"command":"help","timestamp":\$(date +%s)} {"command":"help","timestamp":\$(date +%s)}
Tunnel Control Channel Messages (received from server):
- tunnel_data: Data received from the server
- tunnel_response: Response to tunnel requests
- tunnel_close: Tunnel closure notification
- websocket_message: Other WebSocket messages
- websocket_close: WebSocket connection closed
Examples of expected responses:
{"type":"tunnel_data","message":"...","timestamp":1234567890}
{"type":"tunnel_response","message":"...","timestamp":1234567890}
{"type":"tunnel_close","message":"...","timestamp":1234567890}
{"type":"websocket_close","timestamp":1234567890}
EOF EOF
} }
......
...@@ -110,14 +110,19 @@ test_bridge_mode() { ...@@ -110,14 +110,19 @@ test_bridge_mode() {
log_info "Test 1: Sending status command" log_info "Test 1: Sending status command"
send_command '{"command":"status","timestamp":'$(date +%s)'}' >&${wsssht_in} send_command '{"command":"status","timestamp":'$(date +%s)'}' >&${wsssht_in}
sleep 1 sleep 2
read_response read_response
# Test 2: Send a quit command # Test 2: Monitor for tunnel control messages
log_info "Test 2: Sending quit command" log_info "Test 2: Monitoring for tunnel control messages (5 seconds)"
log_info "Note: In a real scenario, you would see tunnel_data, tunnel_response, or tunnel_close messages here"
sleep 5
# Test 3: Send a quit command
log_info "Test 3: Sending quit command"
send_command '{"command":"quit","timestamp":'$(date +%s)'}' >&${wsssht_in} send_command '{"command":"quit","timestamp":'$(date +%s)'}' >&${wsssht_in}
sleep 1 sleep 2
# Read final messages # Read final messages
while read -t 2 -u ${wsssht_out} line 2>/dev/null; do while read -t 2 -u ${wsssht_out} line 2>/dev/null; do
......
...@@ -343,11 +343,15 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -343,11 +343,15 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
pthread_detach(thread); pthread_detach(thread);
} }
// Main bridge loop - handle stdin/stdout communication // 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;
// Frame accumulation buffer for handling partial WebSocket frames
static char frame_buffer[BUFFER_SIZE * 4];
static int frame_buffer_used = 0;
printf("{\"type\":\"ready\",\"message\":\"Bridge mode active\"}\n"); printf("{\"type\":\"ready\",\"message\":\"Bridge mode active\"}\n");
fflush(stdout); fflush(stdout);
...@@ -355,45 +359,136 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w ...@@ -355,45 +359,136 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
// Check for stdin input (JSON commands) // Check for stdin input (JSON commands)
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds); FD_SET(STDIN_FILENO, &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 = 100000; // 100ms timeout tv.tv_usec = 50000; // 50ms timeout
int retval = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); int max_fd = (ssl_fd > STDIN_FILENO) ? ssl_fd : STDIN_FILENO;
if (retval > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { int retval = select(max_fd + 1, &readfds, NULL, NULL, &tv);
// Read JSON command from stdin
if (fgets(buffer, sizeof(buffer), stdin) == NULL) { if (retval > 0) {
// EOF or error on stdin // Handle stdin input (JSON commands)
printf("{\"type\":\"stdin_closed\"}\n"); if (FD_ISSET(STDIN_FILENO, &readfds)) {
if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
// EOF or error on stdin
printf("{\"type\":\"stdin_closed\"}\n");
fflush(stdout);
break;
}
// Remove trailing newline
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
}
// Parse and handle JSON command
printf("{\"type\":\"command_received\",\"command\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL));
fflush(stdout); fflush(stdout);
break;
}
// Remove trailing newline // For now, just echo back - in full implementation would parse JSON and handle commands
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
} }
// Parse and handle JSON command // Handle WebSocket data (tunnel control messages)
printf("{\"type\":\"command_received\",\"command\":\"%s\"}\n", buffer); if (ssl_fd >= 0 && FD_ISSET(ssl_fd, &readfds)) {
fflush(stdout); pthread_mutex_lock(&tunnel_mutex);
SSL *current_ssl = active_tunnel ? active_tunnel->ssl : NULL;
pthread_mutex_unlock(&tunnel_mutex);
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) {
frame_buffer_used += bytes_read;
if (config->debug) {
printf("[DEBUG - Bridge] Accumulated %d bytes from WebSocket\n", frame_buffer_used);
fflush(stdout);
}
// Try to parse WebSocket frame
char *payload;
int payload_len;
if (parse_websocket_frame(frame_buffer, frame_buffer_used, &payload, &payload_len)) {
// Frame is complete, determine frame type
unsigned char frame_type = frame_buffer[0] & 0x8F;
if (frame_type == 0x81 || frame_type == 0x82) { // Text or binary frame
// Copy payload to buffer for processing
if ((size_t)payload_len < sizeof(buffer)) {
memcpy(buffer, payload, payload_len);
buffer[payload_len] = '\0';
// Handle tunnel control messages
if (strstr(buffer, "tunnel_data")) {
printf("{\"type\":\"tunnel_data\",\"message\":\"%s\",\"timestamp\":%ld}\n", buffer, time(NULL));
fflush(stdout);
} 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);
}
}
} else if (frame_type == 0x88) { // Close frame
printf("{\"type\":\"websocket_close\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
}
// For now, just echo back - in full implementation would parse JSON and handle commands // Remove processed frame from buffer
int frame_size = (payload - frame_buffer) + payload_len;
if (frame_size < frame_buffer_used) {
memmove(frame_buffer, frame_buffer + frame_size, frame_buffer_used - frame_size);
frame_buffer_used -= frame_size;
} else {
frame_buffer_used = 0;
}
}
} else if (bytes_read == 0) {
// Connection closed
printf("{\"type\":\"websocket_connection_closed\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
break;
}
}
}
} }
// Check tunnel status // Check tunnel status
pthread_mutex_lock(&tunnel_mutex); pthread_mutex_lock(&tunnel_mutex);
int tunnel_active = active_tunnel && active_tunnel->active; int tunnel_active = active_tunnel && active_tunnel->active;
int tunnel_broken = active_tunnel && active_tunnel->broken;
pthread_mutex_unlock(&tunnel_mutex); pthread_mutex_unlock(&tunnel_mutex);
if (!tunnel_active) { if (!tunnel_active) {
printf("{\"type\":\"tunnel_closed\"}\n"); if (tunnel_broken) {
printf("{\"type\":\"tunnel_broken\",\"timestamp\":%ld}\n", time(NULL));
} else {
printf("{\"type\":\"tunnel_closed\",\"timestamp\":%ld}\n", time(NULL));
}
fflush(stdout); fflush(stdout);
break; break;
} }
// Small delay to prevent busy looping // Small delay to prevent busy looping
usleep(50000); // 50ms usleep(25000); // 25ms
} }
// Cleanup // Cleanup
......
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