Fix bridge mode to use SSL for WebSocket communication

- Bridge mode was incorrectly using raw socket operations over SSL connection
- Updated to use SSL_read/SSL_write for all WebSocket communication
- Fixed send_websocket_message to use send_websocket_frame with SSL
- Fixed pong frame sending to use SSL
- Fixed select() and FD_ISSET to monitor correct socket

This resolves the 'send_error' and connection closure issues in bridge mode tests.
parent 84781de4
......@@ -4,12 +4,17 @@ This document provides comprehensive testing tools and examples for the wsssht b
## Overview
Bridge mode allows programmatic control of wsssht tunnels through JSON commands sent via stdin/stdout. In bridge mode:
Bridge mode provides a **pure transport layer** for WebSocket communication. Unlike other modes, bridge mode does NOT handle tunnel logic - it simply establishes a WebSocket connection and passes ALL messages through stdin/stdout with proper channel identification.
- **Tunnel control channel messages** are communicated through stdin/stdout (JSON protocol)
- **Tunnel data channel messages** remain handled by wsssht (normal forwarding)
- External applications can control tunnel behavior via JSON commands
- Real-time status updates and responses are provided
In bridge mode:
- **Pure Transport Layer**: Establishes WebSocket connection to wssshd server
- **Message Passing**: Passes ALL messages from stdin to WebSocket (to server) with channel routing
- **Response Handling**: Passes ALL messages from WebSocket to stdout (from server) with channel identification
- **Channel Routing**: Automatically routes data/control messages to appropriate channels
- **No Tunnel Logic**: Does NOT open local ports or handle tunnel-specific logic
- **No Protocol Logic**: Does NOT send tunnel requests or handle tunnel responses
- **Client Responsibility**: All protocol logic must be implemented by the client application
## Test Scripts
......@@ -34,8 +39,12 @@ A comprehensive automated test script that demonstrates basic bridge mode functi
#### Features:
- ✅ Automatic wsssht startup in bridge mode
- ✅ Sends test JSON commands (status, quit)
- ✅ Monitors and displays JSON responses
- ✅ Sends tunnel_request to establish tunnel
- ✅ Waits for tunnel_ack confirmation
- ✅ Sends data message with test string
- ✅ Receives and displays tunnel_response
- ✅ Sends tunnel_close to clean up
- ✅ Monitors and displays all JSON responses
- ✅ Colored output for easy reading
- ✅ Error handling and process management
- ✅ Configurable via environment variables
......@@ -75,36 +84,41 @@ An interactive testing tool that allows manual JSON command input and real-time
- ✅ JSON validation
- ✅ Process management and cleanup
## JSON Command Examples
## Message Format Examples
### Basic Commands
### Messages Sent to Server (via stdin)
Any text string is forwarded to the server with automatic channel detection:
#### Status Check
```json
{"command":"status","timestamp":1640995200}
```
**Response:**
#### Control Channel Messages
```json
{"type":"tunnel_status","active":true,"timestamp":1640995200}
{"type":"tunnel_request","client_id":"myclient","timestamp":1640995200}
```
**Automatically detected as control channel and sent to control WebSocket channel**
#### Quit/Exit
#### Data Channel Messages
```json
{"command":"quit","timestamp":1640995200}
{"type":"tunnel_data","data":"base64_encoded_data","timestamp":1640995200}
```
**Response:**
**Automatically detected as data channel and sent to data WebSocket channel**
### Messages Received from Server (via stdout)
All messages are wrapped with channel identification:
#### Control Channel Response
```json
{"type":"command_received","command":"{\"command\":\"quit\",\"timestamp\":1640995200}","timestamp":1640995200}
{"type":"bridge_ended","timestamp":1640995200}
{"type":"message_received","channel":"control","message":"{\"type\":\"tunnel_response\",\"status\":\"success\"}","timestamp":1640995200}
```
#### Ping
#### Data Channel Response
```json
{"command":"ping","timestamp":1640995200}
{"type":"message_received","channel":"data","message":"base64_encoded_response_data","timestamp":1640995200}
```
**Response:**
#### WebSocket Events
```json
{"type":"command_received","command":"{\"command\":\"ping\",\"timestamp\":1640995200}","timestamp":1640995200}
{"type":"websocket_connected","socket":5,"timestamp":1640995200}
{"type":"ping_received","timestamp":1640995200}
{"type":"pong_received","timestamp":1640995200}
```
### Tunnel Control Channel Messages
......@@ -166,27 +180,36 @@ These are messages received from the wssshd server and forwarded as JSON through
### 2. Initial Responses
Bridge mode sends initial status messages:
```json
{"type":"status","message":"Bridge mode started","client_id":"myclient","host":"mbetter.nexlab.net","port":9898}
{"type":"tunnel_established","listen_sock":5}
{"type":"connection_accepted","socket":6}
{"type":"ready","message":"Bridge mode active"}
{"type":"bridge_started","client_id":"myclient","host":"mbetter.nexlab.net","port":9898,"timestamp":1640995200}
{"type":"websocket_connected","socket":5,"timestamp":1640995200}
{"type":"bridge_ready","message":"Pure transport layer active","timestamp":1640995200}
```
### 3. Command/Response Cycle
Send JSON commands via stdin, receive responses via stdout:
### 3. Message Exchange
Send protocol messages via stdin, receive responses via stdout:
```bash
# Send command
echo '{"command":"status","timestamp":1640995200}' | ./wssshtools/wsssht --bridge --clientid myclient --wssshd-host mbetter.nexlab.net
# Send tunnel request (control channel)
echo '{"type":"tunnel_request","client_id":"myclient","request_id":"req123","tunnel":"any","tunnel_control":"any","service":"ssh","timestamp":1640995200}' | ./wssshtools/wsssht --bridge --clientid myclient --wssshd-host mbetter.nexlab.net
# Receive tunnel acknowledgment (control channel)
{"type":"message_received","channel":"control","message":"{\"type\":\"tunnel_ack\",\"request_id\":\"req123\"}","timestamp":1640995200}
# Send data (data channel) - hex-encoded
echo '{"type":"tunnel_data","request_id":"req123","data":"48656c6c6f","timestamp":1640995200}' | ./wssshtools/wsssht --bridge --clientid myclient --wssshd-host mbetter.nexlab.net
# Receive response
{"type":"tunnel_status","active":true,"timestamp":1640995200}
# Receive data response (data channel)
{"type":"message_received","channel":"data","message":"{\"type\":\"tunnel_response\",\"request_id\":\"req123\",\"data\":\"response_data\"}","timestamp":1640995200}
# Send tunnel close (control channel)
echo '{"type":"tunnel_close","request_id":"req123","timestamp":1640995200}' | ./wssshtools/wsssht --bridge --clientid myclient --wssshd-host mbetter.nexlab.net
```
### 4. Tunnel Operation
### 4. Transport Layer Operation
While bridge mode is active:
- **Data Channel**: wsssht handles actual tunnel data forwarding
- **Control Channel**: JSON commands control tunnel behavior
- **Status Updates**: Real-time status information via stdout
- **Pure Transport**: Only handles WebSocket connection and message routing
- **Channel Routing**: Automatically detects and routes messages to control/data channels
- **No Protocol Logic**: Does NOT interpret or handle tunnel protocol
- **Client Responsibility**: Your application implements all protocol logic
## Testing Scenarios
......@@ -295,14 +318,14 @@ DEBUG=1 ./test_bridge_interactive.sh start
## Integration Examples
### Python Integration
### Python Integration (Pure Transport Layer)
```python
import subprocess
import json
import time
def send_bridge_command(command):
"""Send JSON command to wsssht bridge mode"""
def send_protocol_message(message_type, **kwargs):
"""Send protocol message to wsssht bridge mode (pure transport layer)"""
cmd = [
'./wssshtools/wsssht',
'--bridge',
......@@ -318,30 +341,43 @@ def send_bridge_command(command):
text=True
)
# Send command
json_cmd = json.dumps({
'command': command,
'timestamp': int(time.time())
})
proc.stdin.write(json_cmd + '\n')
# Create protocol message
message = {
'type': message_type,
'timestamp': int(time.time()),
**kwargs
}
json_msg = json.dumps(message)
proc.stdin.write(json_msg + '\n')
proc.stdin.flush()
# Read response
# Read responses (your application handles the protocol)
while True:
response = proc.stdout.readline().strip()
if response:
return json.loads(response)
return None
# Usage
response = send_bridge_command('status')
print(f"Tunnel active: {response.get('active', False)}")
if not response:
break
parsed = json.loads(response)
print(f"Received: {parsed}")
# Your application implements protocol logic here
if parsed['type'] == 'message_received':
server_msg = json.loads(parsed['message'])
if server_msg.get('type') == 'tunnel_response':
print("Tunnel established!")
return True
elif server_msg.get('type') == 'error':
print(f"Error: {server_msg.get('message')}")
return False
return False
# Usage - your application handles the protocol
success = send_protocol_message('tunnel_request', client_id='myclient')
```
### Node.js Integration
### Node.js Integration (Pure Transport Layer)
```javascript
const { spawn } = require('child_process');
const fs = require('fs');
function startBridgeMode() {
const wsssht = spawn('./wssshtools/wsssht', [
......@@ -350,18 +386,30 @@ function startBridgeMode() {
'--wssshd-host', 'mbetter.nexlab.net'
]);
// Send command
const command = JSON.stringify({
command: 'status',
// Send protocol message (your application handles the protocol)
const tunnelRequest = JSON.stringify({
type: 'tunnel_request',
client_id: 'myclient',
timestamp: Math.floor(Date.now() / 1000)
});
wsssht.stdin.write(command + '\n');
wsssht.stdin.write(tunnelRequest + '\n');
// Handle responses
// Handle responses (your application handles the protocol)
wsssht.stdout.on('data', (data) => {
const response = JSON.parse(data.toString().trim());
console.log('Response:', response);
console.log('Received:', response);
// Your application implements protocol logic here
if (response.type === 'message_received') {
const serverMessage = JSON.parse(response.message);
if (serverMessage.type === 'tunnel_response') {
console.log('Tunnel established!');
} else if (serverMessage.type === 'tunnel_data') {
// Handle tunnel data
console.log('Received data:', serverMessage.data);
}
}
});
wsssht.stderr.on('data', (data) => {
......@@ -371,7 +419,7 @@ function startBridgeMode() {
return wsssht;
}
// Usage
// Usage - your application handles the protocol
const proc = startBridgeMode();
// Cleanup on exit
......@@ -383,15 +431,15 @@ process.on('SIGINT', () => {
## Best Practices
### 1. Command Format
- Always include `timestamp` field for tracking
- Use valid JSON format
- Handle responses asynchronously
### 1. Message Format
- Any text format is supported (JSON, plain text, custom protocols)
- Handle responses asynchronously in your application
- Implement proper message framing for your protocol
### 2. Error Handling
- Check process exit codes
- Monitor for timeout scenarios
- Validate JSON responses
- Monitor for WebSocket connection status
- Validate message formats in your application
### 3. Resource Management
- Properly terminate wsssht processes
......@@ -399,9 +447,14 @@ process.on('SIGINT', () => {
- Clean up file descriptors
### 4. Security
- Validate input data
- Use secure communication channels
- Implement proper authentication
- Validate input data in your application
- Use secure WebSocket connections (wss://)
- Implement proper authentication in your protocol
### 5. Channel Management
- Use appropriate message types for control vs data channels
- Handle channel-specific routing in your application
- Monitor both channels for complete protocol implementation
## Files Created
......
......@@ -106,20 +106,77 @@ test_bridge_mode() {
fi
done
# Test 1: Send a status command
log_info "Test 1: Sending status command"
send_command '{"command":"status","timestamp":'$(date +%s)'}' >&${wsssht_in}
# Generate a unique request ID for the tunnel
REQUEST_ID="test_$(date +%s)_$$"
# Test 1: Send tunnel request
log_info "Test 1: Sending tunnel request"
send_command '{"type":"tunnel_request","client_id":"'$CLIENT_ID'","request_id":"'$REQUEST_ID'","tunnel":"any","tunnel_control":"any","service":"ssh","timestamp":'$(date +%s)'}' >&${wsssht_in}
# Wait for tunnel acknowledgment
log_info "Waiting for tunnel acknowledgment..."
local tunnel_ack_received=false
local timeout_counter=0
while [ $timeout_counter -lt 10 ]; do # 10 second timeout
if read -t 1 -u ${wsssht_out} line 2>/dev/null; then
if [ -n "$line" ]; then
log_info "Response: $line"
# Check if we received tunnel_ack
if echo "$line" | grep -q '"message".*"tunnel_ack"'; then
log_success "Tunnel acknowledgment received!"
tunnel_ack_received=true
break
fi
fi
fi
timeout_counter=$((timeout_counter + 1))
done
sleep 2
read_response
if [ "$tunnel_ack_received" = false ]; then
log_error "Timeout waiting for tunnel acknowledgment"
# Send quit to clean up
send_command '{"command":"quit","timestamp":'$(date +%s)'}' >&${wsssht_in}
return 1
fi
# Test 2: Send data message with text string
log_info "Test 2: Sending data message with text string"
TEST_MESSAGE="Hello from bridge mode test!"
# Convert to hex for tunnel_data format
HEX_MESSAGE=$(echo -n "$TEST_MESSAGE" | xxd -p | tr -d '\n')
send_command '{"type":"tunnel_data","request_id":"'$REQUEST_ID'","data":"'$HEX_MESSAGE'","timestamp":'$(date +%s)'}' >&${wsssht_in}
# Wait for tunnel response
log_info "Waiting for tunnel response..."
local tunnel_response_received=false
timeout_counter=0
while [ $timeout_counter -lt 10 ]; do # 10 second timeout
if read -t 1 -u ${wsssht_out} line 2>/dev/null; then
if [ -n "$line" ]; then
log_info "Response: $line"
# Check if we received tunnel_response
if echo "$line" | grep -q '"message".*"tunnel_response"'; then
log_success "Tunnel response received!"
tunnel_response_received=true
break
fi
fi
fi
timeout_counter=$((timeout_counter + 1))
done
if [ "$tunnel_response_received" = false ]; then
log_warning "No tunnel response received within timeout"
fi
# Test 2: Monitor for tunnel control messages
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 tunnel close
log_info "Test 3: Sending tunnel close"
send_command '{"type":"tunnel_close","request_id":"'$REQUEST_ID'","timestamp":'$(date +%s)'}' >&${wsssht_in}
sleep 2
# Test 3: Send a quit command
log_info "Test 3: Sending quit command"
# Test 4: Send quit command to end bridge mode
log_info "Test 4: Sending quit command to end bridge mode"
send_command '{"command":"quit","timestamp":'$(date +%s)'}' >&${wsssht_in}
sleep 2
......
......@@ -297,6 +297,16 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
return 1;
}
// Get the SSL connection for sending messages
SSL *ws_ssl = active_tunnel ? active_tunnel->ssl : NULL;
if (!ws_ssl) {
printf("{\"type\":\"error\",\"message\":\"Failed to get SSL connection\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
close(ws_sock);
if (ws_ctx) SSL_CTX_free(ws_ctx);
return 1;
}
// Send connection established message
printf("{\"type\":\"websocket_connected\",\"socket\":%d,\"timestamp\":%ld}\n", ws_sock, time(NULL));
fflush(stdout);
......@@ -349,8 +359,8 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
channel = "data";
}
// Send message to appropriate WebSocket channel
if (send_websocket_message(ws_sock, buffer, len, channel, config->debug) == 0) {
// Send message to appropriate WebSocket channel using SSL
if (send_websocket_frame(ws_ssl, buffer)) {
printf("{\"type\":\"message_sent\",\"channel\":\"%s\",\"message\":\"%s\",\"timestamp\":%ld}\n",
channel, buffer, time(NULL));
fflush(stdout);
......@@ -364,8 +374,9 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
// Handle WebSocket data (messages from server)
if (FD_ISSET(ws_sock, &readfds)) {
if ((size_t)frame_buffer_used < sizeof(frame_buffer)) {
int bytes_read = recv(ws_sock, frame_buffer + frame_buffer_used,
sizeof(frame_buffer) - frame_buffer_used, 0);
// Use SSL to read WebSocket data
int bytes_read = SSL_read(ws_ssl, frame_buffer + frame_buffer_used,
sizeof(frame_buffer) - frame_buffer_used);
if (bytes_read > 0) {
frame_buffer_used += bytes_read;
......@@ -406,7 +417,10 @@ int run_bridge_mode(wsssh_config_t *config, const char *client_id, const char *w
printf("{\"type\":\"ping_received\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
// Send pong
send_pong_frame_ws(ws_sock, payload, payload_len);
if (!send_pong_frame(ws_ssl, payload, payload_len)) {
printf("{\"type\":\"pong_send_error\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
}
} else if (frame_type == 0x8A) { // Pong frame
printf("{\"type\":\"pong_received\",\"timestamp\":%ld}\n", time(NULL));
fflush(stdout);
......
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